StackTraceがないJavaのNullPointerException


332

JavaコードのインスタンスでをキャッチしましたNullPointerExceptionが、StackTrace(基本的にはを呼び出すThrowable.printStackTrace())をログに記録しようとすると、次のようになります。

java.lang.NullPointerException

他の誰かがこれに遭遇しましたか?「java nullポインターが空のスタックトレース」をグーグルで試しましたが、このようなことは何も見つかりませんでした。


コンテキストとは何ですか?複数のスレッドが関係していますか?SwingWorkerで例外のスタックトレースを取得しようとして問題が発生しました。
マイケルマイヤーズ

ここではスレッド化は行われず、古いJavaが使用されます。
Edward Shtern

1
@Bozho-いいえ-NullPointerの再現方法はまだわかりません。
Edward Shtern


詳細-XX:-OmitStackTraceInFastThrowDUPで:stackoverflow.com/questions/4659151/...
ヴァジム・

回答:


406

おそらく、多くの最適化を実行するHotSpot JVM(当初はSun Microsystems、後にOracleが購入し、OpenJDKの一部)を使用しています。スタックトレースを取得するには、オプション-XX:-OmitStackTraceInFastThrowをJVM に渡す必要があります。

最適化は、例外(通常はNullPointerException)が初めて発生したときに、完全なスタックトレースが出力され、JVMがスタックトレース(またはコードの場所のみ)を記憶することです。この例外が頻繁に発生すると、スタックトレースは出力されなくなり、パフォーマンスが向上し、ログに同一のスタックトレースが殺到することもなくなります。

これがHotSpot JVMでどのように実装されているかを確認するには、そのコピーを取得して、グローバル変数を検索しますOmitStackTraceInFastThrow。前回コードを調べたとき(2019年)、それはファイルgraphKit.cppにありました


1
先端をありがとう。このオプションを渡すことに隠された落とし穴があるかどうかのアイデアはありますか(私のアプリケーションが大量の例外をスローしない限り、それはかなり無害に思えます)?
Edward Shtern

私が知っている隠れた落とし穴はありません。ホットスポットのソースコードを見ると、このオプションが1か所(graphKit.cpp)でのみ使用されていることがわかります。そして、それは私には元気に見えます。
Roland Illig 2010年

34
スタックトレースが最適化されなくなると、少なくとも1回は完全に処理されたため、情報を追加すると思います。jawspeak.com
2010/05/26

1
私はOpenJDK JVM、バージョン1.8.0u171(Debian 9)を実行し-XX:-OmitStackTraceInFastThrowていますが、フラグも受け入れているようです。スタックトレースの印刷にも失敗したのはそのためかどうかをまだ確認していません(たとえばを使用e.printStackTrace)。しかし、それは可能性が高いようです。この発見を反映するように答えを広げました。
Chris W.

今回のケースでは、最初の125の例外にスタックトレースがあり、その後、ログファイルの3ローテーション全体の残りには何もありませんでした。この回答は、犯人を見つけるのに非常に役立ちました。
sukhmel

61

コメントで述べたように、log4jを使用しています。(うっかり)書いた場所を発見した

LOG.error(exc);

典型的なの代わりに

LOG.error("Some informative message", e);

怠惰を介して、またはおそらくそれについて単に考えていません。残念なことに、期待どおりに動作しません。ロガーAPIは実際には文字列ではなくObjectを最初の引数として受け取り、次に引数に対してtoString()を呼び出します。したがって、きれいなスタックトレースを取得する代わりに、toStringを出力するだけです。これは、NPEの場合はまったく役に立ちません。

おそらくこれはあなたが経験していることですか?


+1:これは説明された動作を説明するものであり、これを発見したのはあなただけではありません:)
Peter Lang

4
上記の最初のフォーム(LOG.error(exc);)を絶対に使用しないという標準的なポリシーがあります。常に2つのパラメータシグネチャを使用して、生のスタックトレースだけでなく、説明文をログに追加します。
Edward Shtern

5
もちろん、ポリシーは常に正しく実行されるとは限りません!少なくとも言及する価値があると考えました。
Steven Schlansker

本当ですが、この場合は;-)でした
Edward Shtern

28

これと同じ動作が過去にありました。クレイジーな理由で、コードの同じ場所でNullPointerExceptionが複数回発生した場合、しばらくするLog.error(String, Throwable)と、使用すると完全なスタックトレースが含まれなくなることがわかりました。

ログをさらに調べてみてください。あなたは犯人を見つけるかもしれません。

編集: このバグは関連しているようですが、かなり前に修正されたため、おそらく原因ではありません。


2
バグはクローズされていますが、パフォーマンスの最適化を回避するには、-XX:-OmitStackTraceInFastThrowフラグが必要です。
Joshua Goldberg

私は最近これをたくさん見ています。これを引き起こしている可能性があるもの、またはそれを修正する方法についての手がかりはありますか?ロギングシステムは数日間
稼働し

5
パウェル、-XX:-OmitStackTraceInFastThrowジョシュアが提案したJVMフラグを試しましたか?stackoverflow.com/a/2070568/6198も参照してください。
Matt Solnit 2012

1
これで終わりです。ありがとう。
Andrew Cheong

20

説明は次のとおりです。ホットスポットが原因で例外が発生し、本番環境でスタックトレースが失われました–修正

Mac OS Xでテストしました

  • Javaバージョン「1.6.0_26」
  • Java(TM)SEランタイム環境(ビルド1.6.0_26-b03-383-11A511)
  • Java HotSpot(TM)64ビットサーバーVM(ビルド20.1-b02-383、混合モード)

    Object string = "abcd";
    int i = 0;
    while (i < 12289) {
        i++;
        try {
            Integer a = (Integer) string;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

この特定のコードフラグメントの場合、12288回の反復(+頻度?)は、JVMが事前に割り当てられた例外を使用することを決定した制限のようです...


10

exception.toString StackTraceを提供せず、返すだけです

このスロー可能オブジェクトの短い説明。結果は次の連結です。

* the name of the class of this object
* ": " (a colon and a space)
* the result of invoking this object's getLocalizedMessage() method

exception.printStackTrace代わりにStackTraceを出力するために使用します。


申し訳ありませんが、元の投稿を間違えました。私はこれらをLog4Jを介して記録しています。これは、printStackTrace()を使用します。
Edward Shtern

1
getStackTrace()ロガーに問題がないことを確認するためにを使用してみましたか?
Peter Lang

1
log4jを使用している場合は、必ず引数をlogメソッドの引数の一部として送信してください。それでお答えします。
Ravi Wallau、2010年

@raviaw有効なポイントです!@Edward Shtern:log4jメソッドの2引数形式を確実に使用していることを確認できますか?私はあなたが回答をさらに下で答えたのは会社の方針であると述べたのを知っていますが、この場合はあなたがその方針に従っていることを本当に確信していますか?
KarstenF 2010年

長いショットかもしれませんが、例外がサードパーティのコードに起因している可能性はありますか?多分それは(書き下ろしの)例外ラッパーであり、そのtoString()は単にラップされた例外のクラス名を返し、基になるスタックトレースを提供できません。logger.info( "Exception class =" + exc.class.getCanonicalName())のようなものをキャッチブロックに挿入して、何が得られるかを確認してください。
KarstenF 2010年

4

別の提案-Eclipseを使用している場合は、NullPointerException自体にブレークポイントを設定できます(デバッグパースペクティブで、[ブレークポイント]タブに移動し、!がある小さなアイコンをクリックします)。

「caught」オプションと「uncaught」オプションの両方を確認します。NPEをトリガーするとすぐにブレークポイントが表示され、ステップスルーして、それがどのように正確に処理され、スタックトレースが得られない理由を確認できます。


1

toString()例外名とオプションのメッセージのみを返します。電話することをお勧めします

exception.printStackTrace()

メッセージをダンプするため、または悲惨な詳細が必要な場合:

 StackTraceElement[] trace = exception.getStackTrace()

上記を参照-私は間違って話しています-私はprintStackTrace()を使用しています。
Edward Shtern

1

(あなたのコードはあなたのコードが呼び出しているかどうかについてまだ不明です printStackTrace()ロギングハンドラーによって行われているのです。)

何が起こっているのかについて考えられるいくつかの説明を次に示します。

  • 使用されているロガー/ハンドラーは、完全なスタックトレースではなく、例外のメッセージ文字列のみを出力するように構成されています。

  • アプリケーション(またはいくつかのサードパーティライブラリ)は、 LOG.error(ex);、(たとえば)log4j Loggerメソッドの2引数形式ではなく。

  • メッセージは、あなたが思っている場所とは異なる場所から来ています。たとえば、実際には、サードパーティのライブラリメソッドや、以前のデバッグの試みで残ったランダムなものがあります。

  • ログに記録されている例外により、スタックトレースを不明瞭にするいくつかのメソッドがオーバーロードされました。その場合、例外は本物のNullPointerExceptionではなく、NPEのカスタムサブタイプまたは未接続の例外になります。

最後の可能な説明はかなりありそうもないと思いますが、人々は少なくともリバースエンジニアリングを「防ぐ」ためにこの種のことをすることを考えています。もちろん、それは正直な開発者にとって人生を困難にすることに本当に成功するだけです。


1

プロジェクトでAspectJを使用している場合、一部のアスペクトがスタックトレースのその部分を隠すことがあります。たとえば、今日私は持っていました:

java.lang.NullPointerException:
  at com.company.product.MyTest.test(MyTest.java:37)

このスタックトレースは、Mavenのsurefireを介してテストを実行したときに出力されました。

一方、IntelliJでテストを実行すると、別のスタックトレースが出力されました。

java.lang.NullPointerException
  at com.company.product.library.ArgumentChecker.nonNull(ArgumentChecker.java:67)
  at ...
  at com.company.product.aspects.CheckArgumentsAspect.wrap(CheckArgumentsAspect.java:82)
  at ...
  at com.company.product.MyTest.test(MyTest.java:37)

0

これは例外を出力します。例外をより適切に処理する必要があるデバッグにのみ使用してください。

import java.io.PrintWriter;
import java.io.StringWriter;
    public static String getStackTrace(Throwable t)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        t.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.