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

发布于 2024-11-15 20:35:37 字数 979 浏览 2 评论 0原文

这是一个有趣的java问题。

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

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

有谁知道为什么?

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

Here is a interesting java question.

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.

But when I access the Class, it still available;

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

你又不是我 2024-11-22 20:35:37

此类问题的答案通常隐藏在规范中的某个位置... (§12.4.2)

初始化类时会发生什么:

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

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

6-8 继续初始化,8 执行初始化程序,通常发生在步骤 9 中:

<代码>9。如果初始化程序的执行正常完成,则锁定此 Class 对象,将其标记为完全初始化,通知所有等待线程,释放锁定,并正常完成此过程。

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

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

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

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

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


令人惊讶的部分是第三个打印输出,它显示 MainClass 中的 TestClass.class 实际上保存了对物理 Class 对象的引用。< /p>

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

The answer to such questions is usually buried somewhere in the specs... (§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:

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.

6-8 continue the initialization, 8 executes the initializers, and what usually happens is in step 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.

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.

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

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.)

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


The surprising part is the third printout which shows that TestClass.class in MainClass actually holds a reference to a physical Class object.

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

暮倦 2024-11-22 20:35:37

是的,这通常就是引发 NoClassDefFoundError 的原因。它的名字邪恶,仅此而已。它应该被命名为“类初始化失败异常”或其他名称。

由于具有误导性的名称,遇到此错误的 java 程序员浪费了数百人年的时间试图找出找不到该类的原因。

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

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

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.

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.

池木 2024-11-22 20:35:37

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

这是正确的...

但是当我访问该课程时,它仍然可用

是的。

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

  • 这很难做到,
  • 安全地极其困难,
  • 它会让 JVM 陷入困境。说明应用程序很容易浪费大量时间重复加载和重新加载损坏的代码,而
  • 规范表明(或至少暗示)不应该这样做;详情请参阅其他答案。

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

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

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 extremely difficult 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.

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 Error exceptions 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文