简体   繁体   English

为什么我得到NoClassDefFoundError异常而不是StackOverflow错误?

[英]Why am I getting a NoClassDefFoundError exception rather than a StackOverflow error?

Playing with Java (v9 specifically) I found this situation: 玩Java(特别是v9)我发现了这种情况:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

interface A {
    static A staticMethod() {
        try {
            Method method = A.class.getDeclaredMethods()[0];
            return (A) method.invoke(null);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

public class Test {
    public static void main(String[] args) {
        A.staticMethod();
    }
}

That program flow should cause a StackOverflow error, however, I'm getting a NoClassDefFoundError . 该程序流应该导致StackOverflow错误,但是,我得到一个NoClassDefFoundError

*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 880
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 880
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 880
Exception in thread "main" 
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "main"

According to Javadoc 据Javadoc说

Class NoClassDefFoundError 类NoClassDefFoundError

Thrown if the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found . 如果Java虚拟机或ClassLoader实例尝试加载类的定义(作为普通方法调用的一部分或作为使用新表达式创建新实例的一部分)并且无法找到类的定义,则抛出该异常。

The searched-for class definition existed when the currently executing class was compiled, but the definition can no longer be found . 搜索的类定义在编译当前正在执行的类时存在, 但无法再找到该定义

This is a weird error message, is it a bug? 这是一个奇怪的错误消息,它是一个错误吗?

UPDATE: Bug Report Id: 9052375 更新: 错误报告ID:9052375

Executed from the command line and prints the expected error: The problem was, the exceptions used in catch . 在命令行中执行并打印预期的错误: 问题是,在使用的异常catch

在此输入图像描述

This is not a bug and it also has nothing to do with static methods in interfaces. 这不是错误,它也与接口中的静态方法无关。

The java.lang.instrument ASSERTION FAILED message is also not relevant and is just an artifact of running the code from IDE. java.lang.instrument ASSERTION FAILED消息也不相关,只是从IDE运行代码的工件。 Running the same class from the command line will result in Exception in thread "main" only. 从命令行运行相同的类将仅Exception in thread "main"导致Exception in thread "main"

Lets simplify your example to 让我们简化你的例子

public class Test {
    public static void main( String[] args ) throws Exception {
        recursive();
    }

    public static void recursive() throws Exception {
        try {
            Test.class
                    .getDeclaredMethod( "recursive" )
                    .invoke( null );
        } catch ( InvocationTargetException e ) {
            e.printStackTrace();
        }
    }
}

What is going on: 到底是怎么回事:

  • The recursive method causes StackOverflowError , as expected. 递归方法会导致StackOverflowError ,如预期的那样。
  • The StackOverflowError is wrapped into InvocationTargetException which is thrown from the deepest nested call to method.invoke() . StackOverflowError被包装到InvocationTargetException ,这是从对method.invoke()的最深嵌套调用中抛出的。
  • The InvocationTargetException is immediately caught and JVM tries to execute printStackTrace() but in order to do that it needs to load some classes. 立即捕获InvocationTargetException并且JVM尝试执行printStackTrace()但是为了做到这一点,它需要加载一些类。 But remember that at this point the stack is depleted and any non-trivial methods will hit StackOverflowError again, which is exactly what happens somewhere inside the class loader when it tries to load some class required to print a stack trace. 但请记住,此时堆栈已耗尽,并且任何非平凡的方法将再次遇到StackOverflowError ,这正是类加载器内部尝试加载打印堆栈跟踪所需的类时所发生的情况。 The class loader did found the class, but failed to load and initialize it, and it reports that as NoClassDefFoundError . 类加载器确实找到了类,但无法加载和初始化它,并将其报告为NoClassDefFoundError

The following code will prove that InvocationTargetException indeed wraps StackOverflowError : 以下代码将证明InvocationTargetException确实包装了StackOverflowError

public class Test {
    public static void main( String[] args ) throws Exception {
        recursive();
    }

    public static void recursive() throws Exception {
        try {
            Test.class
                    .getDeclaredMethod( "recursive" )
                    .invoke( null );
        } catch ( InvocationTargetException e ) {
            System.out.println(e);
            System.out.println(e.getTargetException());
        }
    }
}

And the following code will prove that if classes required to execute printStackTrace() are already loaded, then the code behaves as expected (prints a stack trace for InvocationTargetException caused by StackOverflowError : 以下代码将证明如果已经加载了执行printStackTrace()所需的类,则代码将按预期运行(打印由StackOverflowError引起的InvocationTargetException的堆栈跟踪:

public class Test {
    public static void main( String[] args ) throws Exception {
        new Exception().printStackTrace(); // initialize all required classes
        recursive();
    }

    public static void recursive() throws Exception {
        try {
            Test.class
                    .getDeclaredMethod( "recursive" )
                    .invoke( null );
        } catch ( InvocationTargetException e ) {
            e.printStackTrace();
        }
    }
}

The open question is why reflection API handles StackOverflowError at all, instead of simply terminating the whole call chain with the error. 开放式问题是为什么反射API完全处理StackOverflowError ,而不是简单地用错误终止整个调用链。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM