スタックトレースとは何ですか。また、それを使用してアプリケーションエラーをデバッグする方法を教えてください。


643

アプリケーションを実行すると、次のようなエラーが表示されることがあります。

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)

人々はこれを「スタックトレース」と呼んでいます。スタックトレースとは何ですか?プログラムで発生しているエラーについて何がわかりますか?


この質問について-初心者プログラマーが「エラーを取得している」という質問が頻繁に出てきます。スタックトレースとは何か、またはどのように使用するかを理解せずに、スタックトレースとコードのランダムブロックを貼り付けるだけです。この質問は、スタックトレースの値を理解するのに助けを必要とする可能性のある初心者プログラマのためのリファレンスとして意図されています。


25
また、スタックトレース行にファイル名と行番号が含まれていない場合、その行のクラスはデバッグ情報でコンパイルされていません。
–ThorbjørnRavn Andersen 2011

回答:


589

簡単に言えば、スタックトレースは、例外がスローされたときにアプリケーションが途中だったメソッド呼び出しのリストです。

簡単な例

質問の例では、アプリケーションで例外がスローされた場所を正確に判断できます。スタックトレースを見てみましょう。

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)

これは非常に単純なスタックトレースです。「...」のリストの先頭から開始すると、エラーが発生した場所を特定できます。探しているのは、アプリケーションの一部である最上位のメソッド呼び出しです。この場合、それは:

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

これをデバッグするには、次の行を開いてBook.java確認します16

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

これは、何か(おそらくtitle)がnullが上記のコードに含まれてます。

一連の例外の例

アプリケーションが例外をキャッチして、別の例外の原因として再スローする場合があります。これは通常次のようになります。

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   }

これにより、次のようなスタックトレースが得られます。

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

これとの違いは「原因」です。例外には複数の「原因」セクションがある場合があります。これらについては、通常、「根本的な原因」を見つけたいと思います。これは、スタックトレースの最も低い「原因」セクションの1つになります。私たちの場合、それは:

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

繰り返しますが、これは例外で、私たちはラインを見てみたいと思います22Book.java原因となる可能性があるか見てNullPointerExceptionここに。

ライブラリコードを使用したより困難な例

通常、スタックトレースは上記の2つの例よりもはるかに複雑です。次に例を示します(長い例ですが、いくつかのレベルの連鎖例外を示しています)。

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

この例では、もっとたくさんあります。私たちが最も心配しているのは、パッケージ内の任意のものであるコードからのメソッドを探すことですcom.example.myproject。2番目の例(上記)から、最初に根本原因を調べます。

Caused by: java.sql.SQLException

ただし、その下のすべてのメソッド呼び出しはライブラリコードです。したがって、その上の「原因」に移動し、コードから発生した最初のメソッド呼び出しを探します。

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

前の例のように、このエラーが発生した場所であるので、MyEntityService.java行を確認する必要があります59(SQLExceptionがエラーを示すため、これは問題の原因が少し明白ですが、デバッグ手順は後で行います)。


3
@RobHruska-非常によく説明されています。+1。例外トレースを文字列として受け取り、スタックトレースを分析するための有用なメソッドを提供するパーサーを知っていますか?-getLastCausedBy()またはgetCausedByForMyAppCode( "com.example.myproject")と同様
Andy Dufresne

1
@AndyDufresne-私はまだ出会ったことはありませんが、その後、私も実際には見ていません。
Rob Hruska、2014年

1
推奨される改善点:Exception in thread "main"最初の例で始まるスタックトレースの最初の行を説明します。この行には、問題の診断に役立つ変数の値などのメッセージがしばしば付随することを説明すると、特に役立つと思います。自分で編集を試みましたが、これらのアイデアを既存の回答の構造に合わせるのに苦労しています。
Code-Apprentice

5
また、Java 1.7は「Suppressed:」を追加しました-この例外の「原因」を表示する前に、抑制された例外スタックトレースをリストします。これは、try-with-resourceコンストラクトdocs.oracle.com/javase/specs/jls/se8/html/…によって自動的に使用され、リソースのクローズ中にスローされた例外がある場合はそれを含みます。
dhblah

「this.nullInstanceField」がnullであるため、フィールド「nullInstanceField」を書き込めないなどの詳細を提供することにより、特にNPEのわかりやすさをさらに向上させることを目的としたJEP openjdk.java.net/jeps/8220715があります。
Mahatma_Fatal_Error

80

私はこの回答を投稿しているので、最上位の回答(アクティビティで並べ替えた場合)は単なる間違いではありません。

スタックトレースとは何ですか?

スタックトレースは非常に役立つデバッグツールです。キャッチされない例外がスローされたとき(またはスタックトレースが手動で生成されたとき)の呼び出しスタック(つまり、その時点までに呼び出された関数のスタック)が表示されます。エラーが発生した場所を示すだけでなく、プログラムがコードのその場所でどのように終了したかを示すので、これは非常に便利です。これは次の質問につながります:

例外とは何ですか?

例外は、ランタイム環境がエラーの発生を通知するために使用するものです。一般的な例は、NullPointerException、IndexOutOfBoundsException、またはArithmeticExceptionです。これらはそれぞれ、不可能なことをしようとしたときに発生します。たとえば、Nullオブジェクトを逆参照しようとすると、NullPointerExceptionがスローされます。

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.

スタックトレース/例外をどのように処理する必要がありますか?

まず、例外の原因を調べます。例外の名前をグーグルで調べて、その例外の原因を確認してください。ほとんどの場合、不正なコードが原因です。上記の例では、すべての例外は不正なコードが原因です。したがって、NullPointerExceptionの例でaは、その時点でnullにならないようにすることができます。たとえば、a次のようなチェックを初期化または含めることができます。

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

この方法では、問題のある行は実行されません a==null。他の例も同様です。

例外が発生しないことを確認できない場合があります。たとえば、プログラムでネットワーク接続を使用している場合、コンピューターのインターネット接続が失われるのを防ぐことはできません(たとえば、ユーザーがコンピューターのネットワーク接続を切断するのを防ぐことはできません)。この場合、ネットワークライブラリはおそらく例外をスローします。ここで、例外をキャッチし、すべての例外をキャッチするハンドル。つまり、ネットワーク接続の例では、接続を再度開くか、ユーザーなどに通知する必要があります。また、catchを使用するときは常に、キャッチしたい例外のみをキャッチし、次のような広範なcatchステートメントは使用しない処理する必要があります。これは非常に重要です。そうしないと、誤って例外をキャッチして、誤った方法で反応する可能性があります。catch (Exception e)

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

なぜ使用しないのcatch (Exception e)ですか?

簡単な例を使用して、すべての例外をキャッチしない理由を示しましょう。

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

このコードが実行しようとしているのはArithmeticException、0による除算の可能性によって引き起こされた原因をキャッチすることです。しかし、それNullPointerExceptionは、aまたはの場合にスローされる可能性もキャッチしbますnull。つまり、aを取得する可能性がありますが、NullPointerExceptionそれをArithmeticExceptionとして扱い、おそらく間違った処理を行います。最良のケースでは、NullPointerExceptionがあったことをまだ見逃しています。そのようなものはデバッグを非常に難しくするので、それをしないでください。

TLDR

  1. 例外の原因を特定して修正し、例外がまったくスローされないようにします。
  2. 1.が不可能な場合は、特定の例外をキャッチして処理します。

    • try / catchを追加して、例外を無視しないでください!やめろ!
    • は使用せずcatch (Exception e)、常に特定の例外をキャッチします。それはあなたの頭痛の多くを救うでしょう。

1
バグマスキングを避けるべき理由についての良い説明
Sudip Bhandari

2
私はこの回答を投稿しているので、(アクティビティで並べ替えた場合)一番上の回答は明らかに間違っているものではありません。これはおそらく変更されているので、あなたがどちらについて話しているのかわかりません。しかし、受け入れられた答えは
間違いなく

1
私が意味するものは、私が知る限り、今では削除されています。それは基本的に「ただtry {} catch(Exception e){}を入れてすべてのエラーを無視する」と述べました。受け入れられた回答は私の回答よりもはるかに古いため、この問題について少し異なる見方をすることを目指していました。他人の答えをコピーしたり、他の人がすでにうまくカバーしているものをカバーしたりするのは、誰にも役に立たないと思います。
ダッカロン2017

「例外をキャッチしない」と言うのは誤解を招きやすく、これは1つの使用例にすぎません。あなたの例は素晴らしいですが、あなたがあなたのスレッドループのトップにいるところ(実行の内側)はどうですか?常にそこで例外(またはおそらくスロー可能)をキャッチし、それがログに記録されて、目立たないように消えないようにする必要があります(通常、実行からスローされた例外は、そうするようにスレッド/ロガーを設定しない限り、正しく記録されません)。
ビルK

1
マルチスレッドでのみ問題になるため、この特別なケースは含めませんでした。シングルスレッドでは、リークされた例外がプログラムを強制終了し、目に見える形でログに記録されます。誰かが例外を適切に処理する方法を知らない場合、彼らは通常、マルチスレッドの使用方法もまだ知らない。
ダッカロン2017年

21

ロブが言ったことに追加します。アプリケーションにブレークポイントを設定すると、スタックの段階的な処理が可能になります。これにより、開発者はデバッガを使用して、メソッドが予期しない動作をしている正確な時点を確認できます。

RobはNullPointerException(NPE)を使用して一般的なものを説明しているため、次の方法でこの問題の削除を支援できます。

次のようなパラメータを取るメソッドがある場合: void (String firstName)

私たちのコードでfirstNameは、値が含まれていることを評価したいので、次のようにします。if(firstName == null || firstName.equals("")) return;

上記はfirstName、安全でないパラメーターとして使用することを防ぎます。したがって、処理前にnullチェックを行うことで、コードが適切に実行されることを確認できます。メソッドを使用してオブジェクトを利用する例を詳しく見ると、次のようになります。

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

上記はnullをチェックするための適切な順序です。ベースオブジェクト(この場合はdog)から始めて、可能性のツリーをたどり始めて、処理の前にすべてが有効であることを確認します。順序を逆にすると、NPEがスローされる可能性があり、プログラムがクラッシュします。


同意した。このアプローチは、たとえば、nulla NullPointerExceptionが検査されているときに、ステートメント内のどの参照であるかを見つけるために使用できます。
Rob Hruska、

16
文字列を処理するときに、equalsメソッドを使用する場合は、次のように、比較の左側で定数を使用する方がよいと思います。if(firstName == null || firstName.equals( "" ))戻る; 私は常に使用します:if(( "")。equals(firstName))これはNullpointer例外を防ぎます
Torres

15

Throwableファミリーが提供するもう1つのスタックトレース機能があります。スタックトレース情報を操作する可能性があります。

標準的な動作:

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

スタックトレース:

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)

操作されたスタックトレース:

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

スタックトレース:

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

2
私はこれについてどう感じているのかわかりません...スレッドの性質上、新しい開発者には独自のスタックトレースを定義しないように勧めます。
PeonProgrammer

15

名前を理解するには:スタックトレースは、最も表面的な例外(サービスレイヤー例外など)から最も深い例外(データベース例外など)までの例外のリスト(または「原因」のリストと言えます)です。私たちが「スタック」と呼ぶ理由と同じように、スタックは先入れ先出し(FILO)であり、最も深い例外が最初に発生し、次に一連の例外が生成され、一連の結果が生成され、表面の例外が最後でした1つは時間内に発生しましたが、そもそもそれがわかります。

キー1:ここで理解する必要があるトリッキーで重要なことは、最も深い原因が「根本原因」ではない可能性があることです。「不良コード」を記述すると、その層の下にある例外が発生する可能性があるためです。たとえば、不正なSQLクエリは、syndaxエラーではなく、ボトルネックでSQLServerException接続をリセットする可能性があり、スタックのちょうど真ん中にある可能性があります。

-> 真ん中の根本原因を突き止めるのはあなたの仕事です。 ここに画像の説明を入力してください

キー2:別のトリッキーですが重要なことは、各「原因」ブロックの内部にあります。最初の行は最も深いレイヤーであり、このブロックの最初の場所で発生します。例えば、

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は、Bootstrap.java:14によって呼び出されたAuther.java:25によって呼び出されました。Book.java:16が根本的な原因でした。ここに、トレーススタックを時系列で並べ替える図を添付します。 ここに画像の説明を入力してください


8

他の例に追加するために、記号とともに表示れる内部(ネストされた)クラスがあります$。例えば:

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

このスタックトレースになります:

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)

5

他の投稿はスタックトレースとは何かについて説明していますが、操作が難しい場合もあります。

スタックトレースを取得し、例外の原因をトレースしたい場合は、EclipseでJava Stack Trace Consoleを使用することを理解することから始めてください。別のIDEを使用する場合、同様の機能があるかもしれませんが、この答えはEclipseに関するものです。

まず、EclipseプロジェクトですべてのJavaソースにアクセスできることを確認します。

次に、Javaパースペクティブで、[ コンソール ]タブ(通常は下部)をクリックします。コンソールビューが表示されない場合は、メニューオプションの[ ウィンドウ ] -> [ビューの表示]に移動し、[ コンソール]を選択します。

次に、コンソールウィンドウで、次のボタン(右側)をクリックします。

コンソールボタン

ドロップダウンリストから[ Java Stack Trace Console]を選択します。

スタックトレースをコンソールに貼り付けます。次に、ソースコードへのリンクと利用可能な他のソースコードのリストを提供します。

これはあなたが見るかもしれないものです(Eclipseドキュメントからの画像):

Eclipseドキュメントの図

行われた最新のメソッド呼び出しは は、スタック一番上になります。これは一番上の行です(メッセージテキストを除く)。スタックを下に行くと、時間に戻ります。2行目は、1行目を呼び出すメソッドなどです。

オープンソースソフトウェアを使用している場合、調べたい場合は、ソースをダウンロードしてプロジェクトに添付する必要があります。ソースjarをダウンロードし、プロジェクトでReferenced Librariesフォルダーを開いて、オープンソースモジュール(クラスファイルを持つモジュール)のjarを見つけ、右クリックして[ プロパティ ]を選択し、ソースjarをアタッチします

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.