web-dev-qa-db-ja.com

StackTraceがないJavaのNullPointerException

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

Java.lang.NullPointerException

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

280
Edward Shtern

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

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

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

330
Roland Illig

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

LOG.error(exc);

典型的な代わりに

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

怠throughや多分それについて考えていない。これの不幸な部分は、期待どおりに動作しないことです。ロガーAPIは、実際には文字列ではなく最初の引数としてObjectを使用します。その後、引数でtoString()を呼び出します。そのため、Nice prettyスタックトレースを取得する代わりに、toStringを出力するだけです。これは、NPEの場合はほとんど役に立ちません。

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

57

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

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

EDIT:このバグ は適切に聞こえますが、かなり前に修正されたため、おそらく原因ではありません。

25
Matt Solnit

説明は次のとおりです。 ホットスポットにより、実稼働環境でスタックトレースが例外で失われる–および修正

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が事前に割り当てられた例外を使用することを決定した制限のようです...

19

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

StackTraceを出力するには、代わりに exception.printStackTrace を使用してください。

10
Peter Lang

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

「caught」オプションと「uncaught」オプションの両方を確認します。NPEをトリガーするとすぐにブレークポイントが設定され、ステップ実行して、正確に処理され、スタックトレースが取得されない理由を確認できます。

4

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

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

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

一方、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)
1
Roland Illig

(コードがprintStackTrace()を呼び出しているのか、これがロギングハンドラーによって実行されているのかについての疑問はまだ不明です。)

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

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

  • アプリケーション(またはサードパーティライブラリ)は、(たとえば)log4j Loggerメソッドの2引数形式ではなく、LOG.error(ex);を使用して例外をログに記録しています。

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

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

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

1
Stephen C

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

exception.printStackTrace()

メッセージをダンプするか、詳細な情報が必要な場合:

 StackTraceElement[] trace = exception.getStackTrace()
1
Sheldon Young

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

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