Co to jest ślad stosu i jak mogę go użyć do debugowania błędów aplikacji?

644

Czasami po uruchomieniu aplikacji pojawia się błąd, który wygląda następująco:

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Ludzie nazywają to „śladem stosu”. Co to jest ślad stosu? Co może mi powiedzieć o błędzie występującym w moim programie?


O tym pytaniu - dość często widzę pytanie, w którym początkujący programista „dostaje błąd”, a oni po prostu wklejają swój ślad stosu i jakiś losowy blok kodu, nie rozumiejąc, co to jest ślad stosu i jak mogą go użyć. To pytanie jest przeznaczone dla początkujących programistów, którzy mogą potrzebować pomocy w zrozumieniu wartości śledzenia stosu.

Rob Hruska
źródło
25
Ponadto, jeśli linia stacktrace nie zawiera nazwy pliku i numeru linii, klasa dla tej linii nie została skompilowana z informacjami debugowania.
Thorbjørn Ravn Andersen

Odpowiedzi:

589

Mówiąc najprościej, ślad stosu jest listą wywołań metod, które aplikacja znajdowała się w trakcie zgłaszania wyjątku.

Prosty przykład

Na przykładzie podanym w pytaniu możemy dokładnie określić, gdzie wyjątek został zgłoszony w aplikacji. Spójrzmy na ślad stosu:

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Jest to bardzo prosty ślad stosu. Jeśli zaczniemy na początku listy „o…”, możemy stwierdzić, gdzie wystąpił nasz błąd. To, czego szukamy, to najlepsze wywołanie metody, które jest częścią naszej aplikacji. W tym przypadku jest to:

at com.example.myproject.Book.getTitle(Book.java:16)

Aby to debugować, możemy otworzyć się Book.javai spojrzeć na linię 16, która jest:

15   public String getTitle() {
16      System.out.println(title.toString());
17      return title;
18   }

Oznaczałoby to, że coś (prawdopodobnie title) jest nullw powyższym kodzie.

Przykład z łańcuchem wyjątków

Czasami aplikacje przechwytują wyjątek i ponownie zgłaszają go jako przyczynę innego wyjątku. Zwykle wygląda to tak:

34   public void getBookIds(int id) {
35      try {
36         book.getId(id);    // this method it throws a NullPointerException on line 22
37      } catch (NullPointerException e) {
38         throw new IllegalStateException("A book has a null property", e)
39      }
40   }

Może to dać ślad stosu, który wygląda następująco:

Exception in thread "main" java.lang.IllegalStateException: A book has a null property
        at com.example.myproject.Author.getBookIds(Author.java:38)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Caused by: java.lang.NullPointerException
        at com.example.myproject.Book.getId(Book.java:22)
        at com.example.myproject.Author.getBookIds(Author.java:36)
        ... 1 more

To, co różni się w tym, to „Spowodowane przez”. Czasami wyjątki zawierają wiele sekcji „Przyczyną”. W tym przypadku zwykle chcesz znaleźć „główną przyczynę”, która będzie jedną z najniższych sekcji „Spowodowanych przez” w śladzie stosu. W naszym przypadku jest to:

Caused by: java.lang.NullPointerException <-- root cause
        at com.example.myproject.Book.getId(Book.java:22) <-- important line

Ponownie, z tym wyjątkiem, że chcemy, aby spojrzeć na linii 22z Book.java, aby zobaczyć, co może spowodować, że NullPointerExceptiontutaj.

Bardziej zniechęcający przykład z kodem bibliotecznym

Zwykle ślady stosu są znacznie bardziej złożone niż dwa powyższe przykłady. Oto przykład (jest długi, ale pokazuje kilka poziomów wyjątków łańcuchowych):

javax.servlet.ServletException: Something bad happened
    at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:60)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.example.myproject.ExceptionHandlerFilter.doFilter(ExceptionHandlerFilter.java:28)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.example.myproject.OutputBufferFilter.doFilter(OutputBufferFilter.java:33)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: com.example.myproject.MyProjectServletException
    at com.example.myproject.MyServlet.doPost(MyServlet.java:169)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:30)
    ... 27 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.example.myproject.MyEntity]
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:64)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2329)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2822)
    at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321)
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
    at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
    at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
    at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:705)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.java:693)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.java:689)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:344)
    at $Proxy19.save(Unknown Source)
    at com.example.myproject.MyEntityService.save(MyEntityService.java:59) <-- relevant call (see notes below)
    at com.example.myproject.MyServlet.doPost(MyServlet.java:164)
    ... 32 more
Caused by: java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
    at org.hsqldb.jdbc.Util.throwError(Unknown Source)
    at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:57)
    ... 54 more

W tym przykładzie jest o wiele więcej. Najbardziej martwi nas szukanie metod z naszego kodu , które mogłyby znajdować się w com.example.myprojectpakiecie. Z drugiego przykładu (powyżej) chcielibyśmy najpierw spojrzeć w dół na główną przyczynę, którą jest:

Caused by: java.sql.SQLException

Jednak wszystkie wywołania metod w ramach tego kodu są kodami bibliotek. Przejdziemy więc do powyższego „Caused by” i poszukajmy pierwszego wywołania metody pochodzącego z naszego kodu, którym jest:

at com.example.myproject.MyEntityService.save(MyEntityService.java:59)

Podobnie jak w poprzednich przykładach, powinniśmy spojrzeć na MyEntityService.javaon-line 59, ponieważ właśnie tam powstał ten błąd (to jest trochę oczywiste, co poszło nie tak, ponieważ SQLException określa błąd, ale o to chodzi w procedurze debugowania).

Rob Hruska
źródło
3
@RobHruska - Bardzo dobrze wyjaśnione. +1. Czy znasz jakieś parsery, które biorą ślad wyjątku jako ciąg i zapewniają użyteczne metody analizy stacktrace? - jak getLastCausedBy () lub getCausedByForMyAppCode („com.example.myproject”)
Andy Dufresne
1
@AndyDufresne - nie spotkałem żadnego, ale znowu tak naprawdę nie szukałem.
Rob Hruska
1
Sugerowane ulepszenie: wyjaśnij pierwszą linię śladu stosu, która zaczyna się Exception in thread "main"w pierwszym przykładzie. Myślę, że szczególnie pomocne byłoby wyjaśnienie, że temu wierszowi często towarzyszy komunikat, taki jak wartość zmiennej, który może pomóc w zdiagnozowaniu problemu. Sam próbowałem dokonać edycji, ale staram się dopasować te pomysły do ​​istniejącej struktury Twojej odpowiedzi.
Czeladnik
5
Również java 1.7 dodała „Suppressed:” - która wyświetla listę wyłączonych śladów stosu przed wyświetleniem „Caused by:” dla tego wyjątku. Jest automatycznie używany przez konstrukt try-with-resource: docs.oracle.com/javase/specs/jls/se8/html/... i zawiera wyjątki, które zostały zgłoszone podczas zamykania zasobów.
dhblah
Istnieje plik JEP openjdk.java.net/jeps/8220715, który ma na celu dalszą poprawę zrozumiałości szczególnie NPE, zapewniając takie szczegóły, jak „Nie można zapisać pola„ nullInstanceField ”, ponieważ„ this.nullInstanceField ”ma wartość null.”
Mahatma_Fatal_Error
80

Publikuję tę odpowiedź, więc najwyższa odpowiedź (po posortowaniu według aktywności) nie jest po prostu błędna.

Co to jest Stacktrace?

Stacktrace to bardzo pomocne narzędzie do debugowania. Pokazuje stos wywołań (czyli stos funkcji, które zostały wywołane do tego momentu) w momencie zgłoszenia nieprzechwyconego wyjątku (lub czasu wygenerowania śledzenia stosu ręcznie). Jest to bardzo przydatne, ponieważ pokazuje nie tylko miejsce wystąpienia błędu, ale także sposób, w jaki program znalazł się w tym miejscu kodu. To prowadzi do następnego pytania:

Co to jest wyjątek?

Wyjątkiem jest środowisko uruchomieniowe, które informuje o wystąpieniu błędu. Popularne przykłady to NullPointerException, IndexOutOfBoundsException lub ArithmeticException. Każde z nich powstaje, gdy próbujesz zrobić coś, co nie jest możliwe. Na przykład wyjątek NullPointerException zostanie zgłoszony, gdy spróbujesz wyrejestrować obiekt Null:

Object a = null;
a.toString();                 //this line throws a NullPointerException

Object[] b = new Object[5];
System.out.println(b[10]);    //this line throws an IndexOutOfBoundsException,
                              //because b is only 5 elements long
int ia = 5;
int ib = 0;
ia = ia/ib;                   //this line throws an  ArithmeticException with the 
                              //message "/ by 0", because you are trying to
                              //divide by 0, which is not possible.

Jak mam radzić sobie z Stacktraces / Exceptions?

Najpierw dowiedz się, co powoduje wyjątek. Spróbuj wyszukać w Google nazwę wyjątku, aby dowiedzieć się, co jest przyczyną tego wyjątku. W większości przypadków będzie to spowodowane nieprawidłowym kodem. W podanych powyżej przykładach wszystkie wyjątki są spowodowane niepoprawnym kodem. Tak więc dla przykładu NullPointerException możesz upewnić się, że aw tym momencie nigdy nie ma wartości null. Możesz na przykład zainicjować alub dołączyć czek podobny do tego:

if (a!=null) {
    a.toString();
}

W ten sposób linia naruszająca nie jest wykonywana, jeśli a==null. To samo dotyczy innych przykładów.

Czasami nie możesz się upewnić, że nie otrzymasz wyjątku. Na przykład, jeśli korzystasz z połączenia sieciowego w swoim programie, nie możesz powstrzymać komputera przed utratą połączenia internetowego (np. Nie możesz powstrzymać użytkownika przed rozłączeniem połączenia sieciowego komputera). W takim przypadku biblioteka sieciowa prawdopodobnie zgłosi wyjątek. Teraz powinieneś złapać wyjątek i go obsłużyć . Oznacza to, że w przykładzie z połączeniem sieciowym powinieneś spróbować ponownie otworzyć połączenie lub powiadomić użytkownika lub coś w tym rodzaju. Ponadto, ilekroć korzystasz z catch, zawsze wychwytuj tylko wyjątek, który chcesz złapać, nie używaj instrukcji typu catch broad, takich jakcatch (Exception e)to złapałoby wszystkie wyjątki. Jest to bardzo ważne, ponieważ w przeciwnym razie możesz przypadkowo złapać niewłaściwy wyjątek i zareagować w niewłaściwy sposób.

try {
    Socket x = new Socket("1.1.1.1", 6789);
    x.getInputStream().read()
} catch (IOException e) {
    System.err.println("Connection could not be established, please try again later!")
}

Dlaczego nie powinienem używać catch (Exception e)?

Użyjmy małego przykładu, aby pokazać, dlaczego nie powinieneś po prostu złapać wszystkich wyjątków:

int mult(Integer a,Integer b) {
    try {
        int result = a/b
        return result;
    } catch (Exception e) {
        System.err.println("Error: Division by zero!");
        return 0;
    }
}

To, co ten kod próbuje zrobić, to złapanie ArithmeticExceptionprzyczyny spowodowanej możliwym dzieleniem przez 0. Ale łapie także możliwe, NullPointerExceptionktóre zostanie wyrzucone, jeśli alub bnull. Oznacza to, że możesz dostać a, NullPointerExceptionale potraktujesz to jako wyjątek arytmetyczny i prawdopodobnie zrobisz coś złego. W najlepszym przypadku nadal brakuje Ci wyjątku NullPointerException. Takie rzeczy znacznie utrudniają debugowanie, więc nie rób tego.

TLDR

  1. Dowiedz się, co jest przyczyną wyjątku i napraw go, aby w ogóle nie zgłaszał wyjątku.
  2. Jeśli 1. nie jest możliwe, złap konkretny wyjątek i obsłuż go.

    • Nigdy nie dodawaj tylko try / catch, a następnie zignoruj ​​wyjątek! Nie rób tego!
    • Nigdy nie używaj catch (Exception e), zawsze wychwytuj określone wyjątki. Dzięki temu zaoszczędzisz wiele bólów głowy.
Dakkaron
źródło
1
miłe wyjaśnienie, dlaczego powinniśmy unikać maskowania błędów
Sudip Bhandari,
2
Publikuję tę odpowiedź, więc najwyższa odpowiedź (posortowana według aktywności) nie jest po prostu błędna. Nie mam pojęcia, o której mówisz, ponieważ prawdopodobnie zmieniła się już do tej pory. Ale przyjęta odpowiedź jest zdecydowanie bardziej interesująca;)
AxelH
1
Ten, o którym mi chodziło, został już usunięty, o ile wiem. Mówił w zasadzie „po prostu spróbuj {} catch (Exception e) {} i zignoruj ​​wszystkie błędy”. Przyjęta odpowiedź jest znacznie starsza od mojej, więc chciałem dać nieco inne spojrzenie na tę sprawę. Nie sądzę, żeby ktoś pomógł po prostu skopiować czyjąś odpowiedź lub zakryć to, co inni już dobrze opisali.
Dakkaron
Mówienie „Nie wychwytuj wyjątku” jest mylące, to tylko jeden przypadek użycia. Twój przykład jest świetny, ale co powiesz na to, gdzie jesteś na szczycie pętli wątku (przebieg wewnętrzny)? Powinieneś ZAWSZE złapać tam wyjątek (a może Throwable) i zalogować go, aby nie zniknął w niewidoczny sposób (wyjątki generowane z uruchomienia zwykle nie są logowane poprawnie, chyba że skonfigurowałeś do tego wątek / logger).
Bill K
1
Nie uwzględniłem tego specjalnego przypadku, ponieważ ma on znaczenie tylko w przypadku wielowątkowości. W przypadku pojedynczego wątku wyciek wyjątku zabija program i jest rejestrowany w widoczny sposób. Jeśli ktoś nie wie, jak prawidłowo obsługiwać wyjątki, zwykle jeszcze nie wie, jak korzystać z wielowątkowości.
Dakkaron
21

Aby dodać do tego, o czym wspomniał Rob. Ustawienie punktów przerwania w aplikacji umożliwia przetwarzanie stosu krok po kroku. Dzięki temu programiści mogą skorzystać z debugera, aby zobaczyć, w którym momencie metoda robi coś nieoczekiwanego.

Ponieważ Rob wykorzystał NullPointerException(NPE) do zilustrowania czegoś powszechnego, możemy pomóc w usunięciu tego problemu w następujący sposób:

jeśli mamy metodę, która przyjmuje parametry takie jak: void (String firstName)

W naszym kodzie chcielibyśmy ocenić, czy firstNamezawiera wartość, zrobilibyśmy to w następujący sposób:if(firstName == null || firstName.equals("")) return;

Powyższe uniemożliwia nam używanie firstNamejako niebezpiecznego parametru. Dlatego przeprowadzając kontrole zerowe przed przetwarzaniem, możemy pomóc zapewnić prawidłowe działanie naszego kodu. Aby rozwinąć na przykładzie, który wykorzystuje obiekt metodami, możemy zajrzeć tutaj:

if(dog == null || dog.firstName == null) return;

Powyżej jest właściwa kolejność sprawdzania wartości zerowych, zaczynamy od obiektu podstawowego, w tym przypadku psa, a następnie zaczynamy chodzić po drzewie możliwości, aby upewnić się, że wszystko jest poprawne przed przetwarzaniem. Jeśli zamówienie zostanie odwrócone, potencjalnie NPE może zostać wyrzucony, a nasz program ulegnie awarii.

Woot4Moo
źródło
Zgoda. Podejście to można zastosować, aby dowiedzieć się, które odniesienie w instrukcji znajduje nullsię NullPointerExceptionna przykład podczas sprawdzania.
Rob Hruska
16
Jeśli chodzi o String, jeśli chcesz użyć metody equals, myślę, że lepiej jest użyć stałej po lewej stronie porównania, jak poniżej: zamiast: if (firstName == null || firstName.equals ("" )) powrót; Zawsze używam: if ((""). Equals (firstName)) Zapobiega to wyjątkowi Nullpointer
Torres
15

Jest jeszcze jedna funkcja śledzenia stosu oferowana przez rodzinę Throwable - możliwość manipulowania informacjami śledzenia stosu.

Standardowe zachowanie:

package test.stack.trace;

public class SomeClass {

    public void methodA() {
        methodB();
    }

    public void methodB() {
        methodC();
    }

    public void methodC() {
        throw new RuntimeException();
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

Ślad stosu:

Exception in thread "main" java.lang.RuntimeException
    at test.stack.trace.SomeClass.methodC(SomeClass.java:18)
    at test.stack.trace.SomeClass.methodB(SomeClass.java:13)
    at test.stack.trace.SomeClass.methodA(SomeClass.java:9)
    at test.stack.trace.SomeClass.main(SomeClass.java:27)

Manipulowany ślad stosu:

package test.stack.trace;

public class SomeClass {

    ...

    public void methodC() {
        RuntimeException e = new RuntimeException();
        e.setStackTrace(new StackTraceElement[]{
                new StackTraceElement("OtherClass", "methodX", "String.java", 99),
                new StackTraceElement("OtherClass", "methodY", "String.java", 55)
        });
        throw e;
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

Ślad stosu:

Exception in thread "main" java.lang.RuntimeException
    at OtherClass.methodX(String.java:99)
    at OtherClass.methodY(String.java:55)
przemek hertel
źródło
2
Nie wiem, jak się z tym czuję ... Z natury wątku radziłbym nowym deweloperom, aby nie definiowali własnego śladu stosu.
PeonProgrammer
15

Aby zrozumieć nazwę : Śledzenie stosu to lista wyjątków (lub można powiedzieć listę „Przyczyna przez”), od najbardziej wyjątkowego na powierzchni (np. Wyjątek warstwy usług) do najgłębszego (np. Wyjątek bazy danych). Tak jak nazywamy to „stosem”, ponieważ stos jest pierwszym na ostatnim wyjściu (FILO), najgłębszy wyjątek miał miejsce na samym początku, a następnie wygenerowano łańcuch wyjątków z szeregiem konsekwencji, wyjątek powierzchniowy był ostatnim jedno wydarzyło się w czasie, ale przede wszystkim to widzimy.

Klucz 1 : Trudną i ważną rzeczą, którą należy tutaj zrozumieć, jest: najgłębsza przyczyna może nie być „pierwotną przyczyną”, ponieważ jeśli napiszesz jakiś „zły kod”, może to spowodować wyjątek, który jest głębszy niż jego warstwa. Na przykład niepoprawne zapytanie SQL może spowodować reset połączenia SQLServerException na dole zamiast błędu syndax, który może znajdować się na środku stosu.

-> Znajdź główną przyczynę pośrodku, to twoja praca. wprowadź opis zdjęcia tutaj

Klucz 2 : Kolejną trudną, ale ważną rzeczą jest każdy blok „Przyczyna”, pierwsza linia była najgłębszą warstwą i zajmowała pierwsze miejsce dla tego bloku. Na przykład,

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
           at com.example.myproject.Author.getBookTitles(Author.java:25)
               at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Book.java:16 został wywołany przez Auther.java:25, który został wywołany przez Bootstrap.java:14, Book.java:16 był główną przyczyną. Tutaj dołącz diagram posortuj stos śledzenia w kolejności chronologicznej. wprowadź opis zdjęcia tutaj

Kevin Li
źródło
8

Aby dodać do innych przykładów, istnieją wewnętrzne (zagnieżdżone) klasy, które pojawiają się wraz ze $znakiem. Na przykład:

public class Test {

    private static void privateMethod() {
        throw new RuntimeException();
    }

    public static void main(String[] args) throws Exception {
        Runnable runnable = new Runnable() {
            @Override public void run() {
                privateMethod();
            }
        };
        runnable.run();
    }
}

Spowoduje to śledzenie stosu:

Exception in thread "main" java.lang.RuntimeException
        at Test.privateMethod(Test.java:4)
        at Test.access$000(Test.java:1)
        at Test$1.run(Test.java:10)
        at Test.main(Test.java:13)
Eugene S.
źródło
5

Pozostałe posty opisują, co to jest ślad stosu, ale nadal może być ciężko pracować.

Jeśli otrzymujesz śledzenie stosu i chcesz prześledzić przyczynę wyjątku, dobrym punktem wyjścia do zrozumienia jest użycie konsoli śledzenia stosu Java w Eclipse . Jeśli użyjesz innego IDE, może istnieć podobna funkcja, ale ta odpowiedź dotyczy Eclipse.

Po pierwsze, upewnij się, że masz wszystkie źródła Java dostępne w projekcie Eclipse.

Następnie w perspektywie Java kliknij kartę Konsola (zwykle u dołu). Jeśli widok konsoli nie jest widoczny, przejdź do opcji menu Okno -> Pokaż widok i wybierz Konsolę .

Następnie w oknie konsoli kliknij następujący przycisk (po prawej)

Przycisk konsoli

a następnie wybierz Konsolę śledzenia stosu Java z listy rozwijanej.

Wklej ślad stosu do konsoli. Następnie udostępni listę linków do kodu źródłowego i dowolnego innego dostępnego kodu źródłowego.

Oto, co możesz zobaczyć (obraz z dokumentacji Eclipse):

Schemat z dokumentacji Eclipse

Najnowszym wywołaniem metody będzie górna część stosu, która jest górną linią (bez tekstu wiadomości). Schodzenie w dół stosu cofa się w czasie. Druga linia to metoda wywołująca pierwszą linię itp.

Jeśli korzystasz z oprogramowania typu open source, może być konieczne pobranie i dołączenie do projektu źródeł, jeśli chcesz je zbadać. Pobierz słoiki źródłowe, w swoim projekcie otwórz folder Biblioteki referencyjne, aby znaleźć słoik dla modułu open source (ten z plikami klas), a następnie kliknij prawym przyciskiem myszy, wybierz Właściwości i dołącz słoik źródłowy.

rghome
źródło