Java 为什么静态字段初始化失败导致NoClassDefFoundError?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6352215/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 05:14:41  来源:igfitidea点击:

Why NoClassDefFoundError caused by static field initialization failure?

javastaticinitializationnoclassdeffounderror

提问by ext2

Here is a interesting java question.

这是一个有趣的java问题。

the following simple java program contains static field initialized by a method statically. Actually, I force the method which calculate the intiailize value to raise a NullPointException, When I access such a static field, a NoClassDefFoundError will raised. it seems the VM treat the Class is not complete.

以下简单的 java 程序包含由静态方法初始化的静态字段。实际上,我强制计算初始化值的方法引发 NullPointException,当我访问这样的静态字段时,将引发 NoClassDefFoundError。看来VM 对待Class 是不完整的。

But when I access the Class, it still available;

但是当我访问 Class 时,它仍然可用;

Does anyone knows why?

有谁知道为什么?

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

回答by irreputable

Yes, that's usually why NoClassDefFoundErroris raised. It's evillynamed, that's all. It should've been named as "class init failed exception" or something.

是的,这通常NoClassDefFoundError是被提出的原因。它的名字很邪恶,仅此而已。它应该被命名为“类初始化失败异常”之类的。

Becuase of the misleading name, java programmers who got this error wasted hundreds of man years trying to figure out why the class cannot be found.

由于误导性名称,遇到此错误的 Java 程序员浪费了数百年的时间来试图找出为什么找不到该类。

Whenever you see this exception, you should check the log upwards, and try to find out the root cause when the class failed to init.

每当你看到这个异常时,你应该向上检查日志,并尝试找出类初始化失败的根本原因。

回答by trutheality

The answer to such questions is usually buried somewhere in the specs... (§12.4.2)

这些问题的答案通常隐藏在规范的某个地方...... (第 12.4.2 节)

What happens when classes are initialized:

初始化类时会发生什么:

Steps 1-4 are somewhat unrelated to this question. Step 5 here is what triggers the exception:

步骤 1-4 与此问题有些无关。此处的第 5 步是触发异常的原因:

5. If the Class object is in an erroneous state,then initialization is not possible. Release the lock on the Class object and throw a NoClassDefFoundError.

5. 如果 Class 对象处于错误状态,则无法进行初始化。释放对 Class 对象的锁定并抛出 NoClassDefFoundError。

6-8 continue the initialization, 8 executes the initializers, and what usually happens is in step 9:

6-8 继续初始化,8 执行初始化器,通常发生在第 9 步:

9. If the execution of the initializers completes normally, then lock this Class object, label it fully initialized, notify all waiting threads, release the lock, and complete this procedure normally.

9. 如果初始化器的执行正常完成,则锁定这个 Class 对象,标记它完全初始化,通知所有等待的线程,释放锁,并正常完成此过程。

But we got an error in the initializer so:

但是我们在初始化程序中遇到了错误,因此:

10. Otherwise, the initializers must have completed abruptly by throwing some exception E.If the class of E is not Error or one of its subclasses, then create a new instance of the class ExceptionInInitializerError, with E as the argument,and use this object in place of E in the following step. But if a new instance of ExceptionInInitializerError cannot be created because an OutOfMemoryError occurs, then instead use an OutOfMemoryError object in place of E in the following step.

10. 否则,初始化程序必须通过抛出一些异常 E 突然完成。如果 E 的类不是 Error 或其子类之一,则创建类 ExceptionInInitializerError 的新实例,以 E 作为参数,并在适当的位置使用此对象E 在下面的步骤中。但是,如果由于发生 OutOfMemoryError 而无法创建 ExceptionInInitializerError 的新实例,则在接下来的步骤中使用 OutOfMemoryError 对象代替 E。

Yep, we see an ExceptionInInitializerErrorb/c of the null pointer exception.

是的,我们看到ExceptionInInitializerError了空指针异常的b/c。

11. Lock the Class object, label it erroneous,notify all waiting threads, release the lock, and complete this procedure abruptly with reason E or its replacement as determined in the previous step. (Due to a flaw in some early implementations, a exception during class initialization was ignored, rather than causing an ExceptionInInitializerError as described here.)

11. 锁定 Class 对象,将其标记为错误,通知所有等待的线程,释放锁定,并在上一步中确定的原因 E 或其替换的情况下突然完成此过程。(由于一些早期实现中的缺陷,类初始化期间的异常被忽略,而不是像这里描述的那样导致 ExceptionInInitializerError 。)

And then the class is marked erroneous which is why we get the exception from step 5 the second time.

然后该类被标记为错误,这就是我们第二次从第 5 步中得到异常的原因。



The surprising part is the third printout which shows that TestClass.classin MainClassactually holds a reference to a physical Classobject.

令人惊讶的部分是第三个打印输出,它显示TestClass.classinMainClass实际上包含对物理Class对象的引用。

Probably because TestClassstill exists, it's just marked erroneous. It has been already loaded and verified.

可能是因为TestClass仍然存在,只是标记为错误。它已经加载并验证。

回答by Stephen C

When I access such a static field, a NoClassDefFoundError will raised. it seems the VM treat the Class is not complete.

当我访问这样的静态字段时,将引发 NoClassDefFoundError。看来VM 对待Class 是不完整的。

That is correct ...

那是正确的 ...

But when I access the Class, it still available

但是当我访问类时,它仍然可用

Yes.

是的。

The class loader has not tried to remove the broken class because:

类加载器没有尝试删除损坏的类,因为:

  • it would be difficult to do,
  • it would be extremelydifficult to do safely,
  • it would leave the JVM in a state where an application could easily waste lots of time repeatedly loading and reloading broken code, and
  • the specs say (or at least imply) that it shouldn't; see other answers for details.
  • 很难做到,
  • 安全地进行将是极其困难的,
  • 它会使 JVM 处于一种状态,在这种状态下,应用程序很容易浪费大量时间反复加载和重新加载损坏的代码,并且
  • 规范说(或至少暗示)它不应该;有关详细信息,请参阅其他答案。

To get into a state where this inconsistency is visible, your application has to catch ClassDefNotFoundError(or a superclass) and attempted to recover from it. It is a well documented fact that Errorexceptions are generally not recoverable; i.e. if you attempt to recover, the JVM may end up in an inconsistent state. That is what has happened here ... with respect to the classes that were being loaded / initialized.

要进入这种不一致的状态,您的应用程序必须捕获ClassDefNotFoundError(或超类)并尝试从中恢复。Error异常通常是不可恢复的,这是一个有据可查的事实。即,如果您尝试恢复,JVM 可能最终处于不一致状态。这就是这里发生的事情......关于正在加载/初始化的类。