web-dev-qa-db-ja.com

静的フィールドの初期化エラーが原因でNoClassDefFoundErrorが発生するのはなぜですか?

ここに興味深いJava質問があります。

次の簡単なJavaプログラムには、メソッドによって静的に初期化された静的フィールドが含まれています。実際には、初期化値を計算するメソッドにNullPointExceptionを発生させます。そのような静的フィールドにアクセスすると、NoClassDefFoundErrorがVM扱うクラスは完全ではないようです。

しかし、私がクラスにアクセスするとき、それはまだ利用可能です。

誰かが理由を知っていますか?

class TestClass {
    public static TestClass instance = init();

    public static TestClass init() {
       String a = null;
       a.charAt(0); //force a null point exception;
       return new TestClass();
    }
}

class MainClass {
    static public void main(String[] args) {
       accessStatic(); // a ExceptionInInitializerError raised cause by NullPointer
       accessStatic(); //now a NoClassDefFoundError occurs;

       // But the class of TestClass is still available; why?
       System.out.println("TestClass.class=" + TestClass.class);
    }

    static void accessStatic() {
        TestClass a;

        try {
            a = TestClass.instance; 
        } catch(Throwable e) {
            e.printStackTrace();
        }
    }   
}
34
ext2

このような質問への答えは通常、仕様のどこかに埋め込まれています... (§12.4.2)

クラスが初期化されるとどうなるか:

手順1〜4は、この質問とは多少関係ありません。ここでのステ​​ップ5は、例外をトリガーするものです。

5Classオブジェクトが誤った状態にある場合、初期化はできません。 Classオブジェクトのロックを解除し、NoClassDefFoundErrorをスローします。

6-8は初期化を続行し、8は初期化子を実行し、通常はステップ9で発生します。

9。イニシャライザの実行が正常に完了したら、このClassオブジェクトをロックし、完全に初期化したラベルを付け、待機中のすべてのスレッドに通知し、ロックを解放して、この手順を正常に完了します。

しかし、イニシャライザでエラーが発生しました。

10それ以外の場合、イニシャライザはいくつかの例外Eをスローすることによって突然完了している必要があります。EのクラスがErrorまたはそのサブクラスの1つでない場合、Eを引数として、クラスExceptionInInitializerErrorの新しいインスタンスを作成します。次の手順で、このオブジェクトをEの代わりに使用します。ただし、OutOfMemoryErrorが発生したためにExceptionInInitializerErrorの新しいインスタンスを作成できない場合は、代わりに次の手順でEの代わりにOutOfMemoryErrorオブジェクトを使用します。

はい、ヌルポインター例外のExceptionInInitializerError b/cが表示されます。

11。 Classオブジェクトをロックし、誤ったラベルを付けます。待機中のすべてのスレッドに通知し、ロックを解除して、理由Eまたはその置換で決定されたように、この手順を突然完了します。前のステップ。 (いくつかの初期の実装の欠陥のため、ここで説明されているようにExceptionInInitializerErrorを引き起こすのではなく、クラスの初期化中の例外が無視されました。)

そして、クラスにエラーのマークが付けられたため、2回目に手順5で例外が発生します。


意外なのは、TestClass.class in MainClassは実際には物理的なClassオブジェクトへの参照を保持しています。

おそらくTestClassがまだ存在しているため、エラーとしてマークされています。すでにロードされ、検証されています。

34
trutheality

はい、それが通常NoClassDefFoundErrorが発生する理由です。 evillyという名前です。それだけです。 「class init failed exception」などの名前が付けられているはずです。

Javaこのエラーが発生したプログラマは、クラスが見つからない理由を理解しようとして何百年も無駄に費やしました。

この例外が発生した場合は、ログを上方に確認し、クラスの初期化に失敗した根本的な原因を見つけてください。

15
irreputable

このような静的フィールドにアクセスすると、NoClassDefFoundErrorが発生します。 VMクラスの扱いは完全ではないようです。

それは正しいです ...

しかし、私がクラスにアクセスするとき、それはまだ利用可能です

はい。

次の理由により、クラスローダーは壊れたクラスを削除しようとしませんでした。

  • それは難しいでしょう
  • 非常に行うのは難しい安全に
  • それは、JVMが、アプリケーションが壊れたコードを繰り返しロードおよび再ロードする多くの時間を簡単に浪費する可能性がある状態のままにします。
  • 仕様書は、それをしてはいけないと言っている(または少なくとも示唆している)。詳細については、他の回答を参照してください。

この不整合が目に見える状態になるには、アプリケーションがClassDefNotFoundError(またはスーパークラス)をキャッチし、そこからの回復を試みる必要があります。 Error例外は通常回復できないということは、十分に文書化された事実です。つまり、回復しようとすると、JVMが一貫性のない状態になる可能性があります。これがここで起こったことです...ロード/初期化されていたクラスに関して。

3
Stephen C
0
Noypi Gilas