788 Stimmen

Was ist ein Stack-Trace, und wie kann ich ihn zur Fehlersuche in meiner Anwendung verwenden?

Wenn ich meine Anwendung ausführe, erhalte ich manchmal eine Fehlermeldung, die wie folgt aussieht:

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)

Man hat dies als "Stack-Trace" bezeichnet. Was ist ein Stack-Trace? Was kann er mir über den Fehler in meinem Programm sagen?


Zu dieser Frage: Ich erlebe oft, dass ein unerfahrener Programmierer eine Frage stellt, bei der er "einen Fehler erhält", und dann einfach seinen Stack-Trace und einen beliebigen Codeblock einfügt, ohne zu verstehen, was der Stack-Trace ist oder wie er ihn verwenden kann. Diese Frage ist als Referenz für unerfahrene Programmierer gedacht, die Hilfe benötigen, um den Wert eines Stack Trace zu verstehen.

34 Stimmen

Wenn eine Stacktrace-Zeile nicht den Dateinamen und eine Zeilennummer enthält, wurde die Klasse für diese Zeile nicht mit Debug-Informationen kompiliert.

749voto

Rob Hruska Punkte 114761

Einfach ausgedrückt, ein Stapelverfolgung ist eine Liste der Methodenaufrufe, in denen sich die Anwendung befand, als eine Exception ausgelöst wurde.

Einfaches Beispiel

Anhand des in der Frage genannten Beispiels können wir genau feststellen, wo die Ausnahme in der Anwendung ausgelöst wurde. Werfen wir einen Blick auf den Stack-Trace:

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)

Dies ist ein sehr einfacher Stack-Trace. Wenn wir am Anfang der Liste von "at ..." beginnen, können wir feststellen, wo der Fehler aufgetreten ist. Was wir suchen, ist die oberste Methodenaufruf, der Teil unserer Anwendung ist. In diesem Fall ist es:

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

Um dies zu debuggen, können wir folgendes öffnen Book.java und sehen Sie sich die Zeile 16 das ist:

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

Dies würde bedeuten, dass etwas (wahrscheinlich title ) ist null im obigen Code.

Beispiel mit einer Kette von Ausnahmen

Manchmal fangen Anwendungen eine Exception ab und werfen sie als Ursache einer anderen Exception wieder aus. Dies sieht typischerweise so aus:

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   }

Dies könnte einen Stack-Trace ergeben, der wie folgt aussieht:

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

Das Besondere an diesem Fall ist das "Verursacht durch". Manchmal haben Ausnahmen mehrere "Verursacht durch"-Abschnitte. In diesen Fällen wollen Sie in der Regel die "Hauptursache" finden, die einer der untersten "Verursacht durch"-Abschnitte im Stacktrace sein wird. In unserem Fall ist das:

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

Auch bei dieser Ausnahme sollten wir uns die Zeile 22 von Book.java um zu sehen, was die Ursache für die NullPointerException hier.

Ein anspruchsvolleres Beispiel mit Bibliothekscode

Normalerweise sind Stack Traces viel komplexer als die beiden obigen Beispiele. Hier ist ein Beispiel (es ist lang, zeigt aber mehrere Ebenen von verketteten Ausnahmen):

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

In diesem Beispiel gibt es noch viel mehr. Worum es uns hauptsächlich geht, ist die Suche nach Methoden, die von unser Kodex was alles im Bereich der com.example.myproject Paket. Im zweiten Beispiel (oben) würden wir zuerst nach der Grundursache suchen, die lautet:

Caused by: java.sql.SQLException

Alle Methodenaufrufe darunter sind jedoch Bibliothekscode. Wir werden also aufsteigen zum "Verursacht durch" oben und suchen Sie in diesem "Verursacht durch"-Block nach dem der erste Methodenaufruf, der aus unserem Code stammt das ist:

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

Wie in den vorangegangenen Beispielen sollten wir uns ansehen MyEntityService.java im Netz 59 denn dort liegt der Ursprung des Fehlers (hier ist es etwas offensichtlich, was schief gelaufen ist, da die SQLException den Fehler angibt, aber wir wollen ja das Debugging-Verfahren).

99voto

Dakkaron Punkte 5419

Was ist ein Stacktrace?

Ein Stacktrace ist ein sehr hilfreiches Werkzeug zur Fehlersuche. Sie zeigt den Aufrufstapel (d.h. den Stapel der Funktionen, die bis zu diesem Zeitpunkt aufgerufen wurden) zu dem Zeitpunkt, an dem eine nicht abgefangene Ausnahme ausgelöst wurde (oder zu dem Zeitpunkt, an dem der Stacktrace manuell erzeugt wurde). Dies ist sehr nützlich, da es nicht nur zeigt, wo der Fehler aufgetreten ist, sondern auch, wie das Programm an diese Stelle des Codes gelangt ist. Dies leitet zur nächsten Frage über:

Was ist eine Ausnahme?

Eine Exception ist das, was die Laufzeitumgebung verwendet, um Ihnen mitzuteilen, dass ein Fehler aufgetreten ist. Gängige Beispiele sind NullPointerException, IndexOutOfBoundsException oder ArithmeticException. Jede dieser Ausnahmen wird verursacht, wenn Sie versuchen, etwas zu tun, was nicht möglich ist. Eine NullPointerException wird zum Beispiel ausgelöst, wenn Sie versuchen, ein Null-Objekt zu derefenzieren:

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.

Wie sollte ich mit Stacktraces/Ausnahmen umgehen?

Finden Sie zunächst heraus, was die Ursache der Ausnahme ist. Versuchen Sie, den Namen der Ausnahme zu googeln, um herauszufinden, was die Ursache für diese Ausnahme ist. In den meisten Fällen wird sie durch fehlerhaften Code verursacht. In den oben genannten Beispielen werden alle Ausnahmen durch fehlerhaften Code verursacht. Für das Beispiel der NullPointerException könnten Sie also sicherstellen, dass a ist zu diesem Zeitpunkt niemals Null. Sie könnten zum Beispiel Folgendes initialisieren a oder einen Scheck wie diesen einreichen:

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

Auf diese Weise wird die beanstandete Zeile nicht ausgeführt, wenn a==null . Das Gleiche gilt für die anderen Beispiele.

Manchmal kann man nicht sicher sein, dass man nicht eine Ausnahme bekommt. Wenn Sie zum Beispiel eine Netzwerkverbindung in Ihrem Programm verwenden, können Sie nicht verhindern, dass der Computer seine Internetverbindung verliert (z. B. können Sie nicht verhindern, dass der Benutzer die Netzwerkverbindung des Computers trennt). In diesem Fall wird die Netzwerkbibliothek wahrscheinlich eine Ausnahme auslösen. Jetzt sollten Sie die Ausnahme abfangen und Griff es. Das heißt, im Beispiel mit der Netzwerkverbindung sollten Sie versuchen, die Verbindung wieder zu öffnen oder den Benutzer zu benachrichtigen oder ähnliches. Außerdem sollten Sie bei der Verwendung von catch immer nur die Ausnahme abfangen, die Sie abfangen wollen, verwenden Sie keine allgemeinen catch-Anweisungen wie catch (Exception e) die alle Ausnahmen abfangen würde. Dies ist sehr wichtig, da Sie sonst versehentlich die falsche Ausnahme abfangen und auf die falsche Weise reagieren könnten.

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!")
}

Warum sollte ich nicht catch (Exception e) ?

Lassen Sie uns anhand eines kleinen Beispiels zeigen, warum Sie nicht einfach alle Ausnahmen abfangen sollten:

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;
    }
}

Mit diesem Code wird versucht, die ArithmeticException die durch eine mögliche Division durch 0 verursacht wird, aber auch eine mögliche NullPointerException die ausgelöst wird, wenn a ou b sind null . Dies bedeutet, dass Sie möglicherweise eine NullPointerException aber Sie werden es als eine ArithmeticException behandeln und wahrscheinlich das Falsche tun. Im besten Fall übersehen Sie noch, dass es eine NullPointerException gab. Solche Dinge erschweren die Fehlersuche erheblich, also tun Sie das nicht.

TLDR

  1. Finden Sie heraus, was die Ursache für die Ausnahme ist, und beheben Sie sie, damit die Ausnahme nicht mehr ausgelöst wird.
  2. Wenn 1. nicht möglich ist, fangen Sie die spezifische Ausnahme ab und behandeln sie.
    • Fügen Sie niemals einfach ein try/catch hinzu und ignorieren Sie dann die Ausnahme einfach! Tun Sie das nicht!
    • Verwenden Sie niemals catch (Exception e) immer bestimmte Ausnahmen abfangen. Das wird Ihnen eine Menge Kopfschmerzen ersparen.

24voto

Woot4Moo Punkte 23471

Um das zu ergänzen, was Rob erwähnt hat. Das Setzen von Haltepunkten in Ihrer Anwendung ermöglicht eine schrittweise Verarbeitung des Stacks. Dadurch kann der Entwickler den Debugger verwenden, um zu sehen, an welchem genauen Punkt die Methode etwas tut, das nicht vorhergesehen wurde.

Seit Rob die NullPointerException (NPE) zur Veranschaulichung eines allgemeinen Problems, können wir dieses Problem auf folgende Weise beheben:

wenn wir eine Methode haben, die Parameter benötigt wie z.B.: void (String firstName)

In unserem Code würden wir auswerten wollen, dass firstName einen Wert enthält, würden wir dies wie folgt tun: if(firstName == null || firstName.equals("")) return;

Dies hindert uns an der Verwendung von firstName als einen unsicheren Parameter. Indem wir vor der Verarbeitung eine Nullprüfung durchführen, können wir sicherstellen, dass unser Code ordnungsgemäß ausgeführt wird. Um ein Beispiel zu erläutern, das ein Objekt mit Methoden verwendet, können wir hier nachsehen:

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

Wir beginnen mit dem Basisobjekt, in diesem Fall Hund, und gehen dann den Baum der Möglichkeiten abwärts, um sicherzustellen, dass alles gültig ist, bevor wir es verarbeiten. Wäre die Reihenfolge umgekehrt, könnte ein NPE ausgelöst werden und unser Programm würde abstürzen.

21voto

Kevin Punkte 1237

Zum Verständnis des Namens : Ein Stack Trace ist eine Liste von Exceptions (oder man kann sagen eine Liste von "Cause by"), von der oberflächlichsten Exception (z.B. Service Layer Exception) bis zur tiefsten (z.B. Database Exception). Genau wie der Grund, warum wir es "Stack" nennen, ist, weil Stack First in Last out (FILO) ist, die tiefste Ausnahme wurde ganz am Anfang passiert, dann wurde eine Kette von Ausnahmen erzeugt eine Reihe von Folgen, die Oberfläche Ausnahme war die letzte in der Zeit passiert, aber wir sehen es an erster Stelle.

Taste 1 Eine knifflige und wichtige Sache, die hier verstanden werden muss, ist: die tiefste Ursache ist nicht unbedingt die "Grundursache", denn wenn Sie einen "schlechten Code" schreiben, kann dieser eine Ausnahme verursachen, die tiefer liegt als seine Schicht. Zum Beispiel kann eine schlechte SQL-Abfrage einen SQLServerException-Verbindungsreset in der Tiefe verursachen, anstatt eines Syndax-Fehlers, der sich in der Mitte des Stacks befindet.

-> Es ist Ihre Aufgabe, die Ursache in der Mitte zu finden. enter image description here

Taste 2 Eine weitere knifflige, aber wichtige Sache ist, dass in jedem "Ursache durch"-Block die erste Zeile die tiefste Ebene ist und an erster Stelle für diesen Block steht. Zum Beispiel,

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 wurde von Auther.java:25 aufgerufen, das von Bootstrap.java:14 aufgerufen wurde, Book.java:16 war die Ursache. Anbei ein Diagramm, das den Trace-Stack in chronologischer Reihenfolge sortiert. enter image description here

19voto

przemek hertel Punkte 3774

Die Throwable-Familie bietet eine weitere Stacktrace-Funktion - die Möglichkeit manipulieren Stack-Trace-Informationen.

Standardverhalten:

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();
    }
}

Stapelverfolgung:

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)

Manipulierter Stack-Trace:

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();
    }
}

Stapelverfolgung:

Exception in thread "main" java.lang.RuntimeException
    at OtherClass.methodX(String.java:99)
    at OtherClass.methodY(String.java:55)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X