通过 JNI NewObject 创建实例时 jvm.dll 中的异常 0xC0000005

Exception 0xC0000005 in jvm.dll when creating an instance via JNI NewObject

我正在编写 a plug-in for an existing application. Implementation language is C. However, the actual functionality is implemented in Java. For this reason, I am using Java Native Interface (JNI) 从 C 中创建一个 JVM 实例。我可以找到合适的 Java class 并创建一个实例。代码如下所示:

login(uintptr_t connection, const char* username, …) {
    …
    jmethodID constructor = (*env)->GetMethodID(env, ps->class, "<init>", "(JLjava/lang/String;)V");
    jstring jusername = (*env)->NewStringUTF(env, username);
    jobject instance = (*env)->NewObject(env, ps->class, constructor, connection, jusername);

一切正常。

在 Linux。

在Windows,简直一团糟。一旦我尝试创建 Java class 的实例,它就会抛出

EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0813751f, pid=8, tid=0x00000009

。更多详细信息已写入日志文件,但堆栈跟踪除了指向 jvm.dll 中的某处外没有任何帮助。使用调试器单步执行并不是很有见地。请注意,这与 .

不同

几天后,我想通了。

我正在调用的构造函数需要一个参数。类型是 long (Java) 又名 J (JNI 类型签名)又名 jlong (相应的 C 类型)。 C uintptr_tjlong.

兼容

在 Linux 上,我的 uintptr_t 长 8 个字节,因为我在 amd64 环境中使用 64 位应用程序。对于 Windows,应用程序是在 32 位中构建的。结果 uintptr_t 只有 4 个字节长,但 JVM 仍然需要 8 个字节 jlong。然而,NewObject 是一个可变参数函数,不会自动提升,类型安全也得不到保证。

login(uintptr_t connection, const char* username, …) {
    …
    jmethodID constructor = (*env)->GetMethodID(env, ps->class, "<init>", "(JLjava/lang/String;)V");
    jstring jusername = (*env)->NewStringUTF(env, username);
    jlong jconnection = connection;
    jobject instance = (*env)->NewObject(env, ps->class, constructor, jconnection, jusername);

简单地转换为正确的类型就是解决方案。我希望 CallVoidMethod 或任何 Call*Method mentioned in the documentation 也存在这个陷阱。