【问题标题】:Java SE-8 injecting class with JNI DefineClass resulting in NoClassDefFoundJava SE-8 使用 JNI DefineClass 注入类导致 NoClassDefFound
【发布时间】:2015-06-03 14:05:51
【问题描述】:

我正在开发一个应用程序,我在自定义启动器中通过以下方式将一些类直接注入 JVM:

第 1 步:使用 LoadLibrary 加载 jvm dll(C++ 代码)

第 2 步:实际注入类:(注意这使用了引导类加载器)

jclass ljcl_LoadedClass = env->DefineClass(className, NULL, classBytes, classSize);

第 3 步:启动应用程序 jniEnvironment_->CallStaticVoidMethod 实际调用main方法并启动应用程序。

稍后在 Java 代码中,我尝试使用注入的类

mySingleton = MySingletonClass.getInstance();

此行导致 NoClassDefFound 异常,我很难解决。 请注意,相同的代码正在与 Java SE-7 一起使用,因此这可能是由于对垃圾收集方式的一些更改或由于对类加载器的可见性的更改 - 我只是坚持如何调试它并找到原因。

另一个有趣的事情是,在 C++ 代码中(在启动应用程序之前,但加载了 JVM dll 后,以下代码实际上找到了类,我什至可以调用 getInstance 方法:

jclass olDecryptorClass = jniEnvironment_->FindClass("com/foo/bar/MySingletonClass");

【问题讨论】:

    标签: java jvm java-native-interface


    【解决方案1】:

    我认识 Andrea,并且已经发现了原因和解决方案,但不知道为什么 Java 8 会发生这种变化。

    原来的问题中缺少一条关键信息,即使用了 Java 代理,并且代理使用了导致 NoClassDefFoundError 的类。

    我创建了一个非常简单的 C++ 控制台应用程序来启动 JVM,一个简单的代理在调用 premain() 方法时输出到控制台“代理已启动”,然后通过我们的覆盖 @987654323 输出“正在加载类:className” @方法。

    使用 Java 7 时,我们在调试时得到以下信息:

    C++步骤

    options[1].optionString = "-javaagent:simple-agent.jar";
    errorCode = createJavaVM(&javaVM, (void**)&jniEnv, &initArgs);
    

    控制台

    Agent Started
    

    C++步骤

    jclass ljcl_LoadedClass = jniEnv->DefineClass(CLASS_NAME, NULL, ljby_ClassBytes, lji_Size);
    

    控制台

    Loading class: com/foo/bar/JVMDefinedClass
    

    C++步骤

    jniEnv->CallStaticVoidMethod(helloWorldClass, mainMethod, mainArgs);
    

    控制台

    Loading class: com/for/bar/HelloWorld
    Hello World!
    

    在 Java 8 中,这变为:

    C++步骤

    options[1].optionString = "-javaagent:simple-agent.jar";
    errorCode = createJavaVM(&javaVM, (void**)&jniEnv, &initArgs);
    

    控制台

    Agent Started
    Loading class: java/lang/invoke/MethodHandleImpl
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 4 more
    Caused by: java.lang.ClassNotFoundException:
    ... 8 more
    Loading class: java/lang/invoke/MethodHandleImpl$1
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 5 more
    Loading class: java/lang/invoke/MethodHandleImpl$2
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 5 more
    Loading class: java/util/function/Function
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 5 more
    Loading class: java/lang/invoke/MethodHandleImpl$3
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 5 more
    Loading class: java/lang/invoke/MethodHandleImpl$4
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 5 more
    Loading class: java/lang/ClassValue
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 5 more
    Loading class: java/lang/ClassValue$Entry
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 6 more
    Loading class: java/lang/ClassValue$Identity
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 7 more
    Loading class: java/lang/ClassValue$Version
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 7 more
    Loading class: java/lang/invoke/MemberName$Factory
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 5 more
    Loading class: java/lang/invoke/MethodHandleStatics
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 4 more
    Loading class: java/lang/invoke/MethodHandleStatics$1
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 5 more
    Loading class: sun/misc/PostVMInitHook
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    ... 3 more
    

    C++步骤

    jclass ljcl_LoadedClass = jniEnv->DefineClass(CLASS_NAME, NULL, ljby_ClassBytes, lji_Size);
    

    控制台

    Loading class: com/foo/bar/JVMDefinedClass
    

    C++步骤

    jniEnv->CallStaticVoidMethod(helloWorldClass, mainMethod, mainArgs);
    

    控制台

    Loading class: com/objectiflune/simple/TestClass
    java.lang.NoClassDefFoundError: com/foo/bar/JVMDefinedClass
    at com.foo.bar.ClassTransformer.transform(ClassTransformer.java:41)
    at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    ... 13 more
    Hello World!
    

    很容易看出为什么最初的 14 个java/lang 类找不到我们的 JVMDefinedClass,它还没有被定义。但即使在我们定义了它之后,似乎还是有一些连锁反应导致应用程序的其余部分找不到该类。

    因此,解决方案是确保我们的ClassFileTransformer.transform() 方法忽略这些(以及任何)Java 系统类:

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    
        if (className.equals("com/foo/bar/JVMDefinedClass")
         || className.startsWith("com/sun")
         || className.startsWith("java/")
         || className.startsWith("javax/")
         || className.startsWith("org/ietf")
         || className.startsWith("org/jcp")
         || className.startsWith("org/omg")
         || className.startsWith("org/w3c")
         || className.startsWith("org/xml")
         || className.startsWith("sun/")
         || className.startsWith("com/oracle/")
         || className.startsWith("jdk/")
         || className.startsWith("oracle/")
         || className.startsWith("javafx/")
        ) return null;
    //... rest of method ...//
    

    现在我们不处理这 14 个类,不会抛出 NoClassDefFoundError 并且应用程序按预期运行。

    我唯一不明白的是,为什么 Java 8 中的 JVM 现在通过代理发送这 14 个类,而以前没有,以及这是否应该发生。

    【讨论】:

    • 请仅使用发布答案按钮获取实际答案。您应该修改原始问题以添加更多信息。
    • 嗨,我想这可能是在以下的旗帜下:有同样的问题吗?问题仍然没有答案,你有同样的问题吗?通过研究问题帮助我们找到解决方案,然后贡献您的研究结果以及您尝试过的任何其他内容作为部分答案。来自:如何写出好的答案?
    猜你喜欢
    • 2021-05-08
    • 2023-02-08
    • 2016-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多