英文:
Exception 0xC0000005 in jvm.dll when creating an instance via JNI NewObject
问题
我正在为现有应用编写一个插件。实现语言是C。然而,实际功能是用Java实现的。因此,我正在使用Java本机接口(JNI)在C内部创建一个JVM实例。我可以找到适当的Java类并创建一个实例。代码如下所示:
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类的实例,它就会抛出一个
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0813751f, pid=8, tid=0x00000009
更多细节被写入日志文件,但堆栈跟踪除了指向jvm.dll中的某个位置外并不提供有用的信息。使用调试器逐步进行调试并没有带来洞察力。请注意,这与此问题不同。
英文:
I am writing 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) to create a JVM instance from within C. I can find the appropriate Java class and create an instance. This is what the code looks like:
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);
Everything works just fine.
On Linux.
On Windows, it is a complete mess. As soon as I try to create an instance of the Java class, it throws a
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0813751f, pid=8, tid=0x00000009
. More details are written to a log file, but the stack trace is not helpful other than pointing to somewhere in the jvm.dll. Stepping through with a debugger has not been insightful. Note this is not the same as this question.
答案1
得分: 3
经过几天的研究,我找到了解决方法。
我正在调用的构造函数需要一个参数。类型为 long
(Java),也称为 J
(JNI 类型签名),又或者称为 jlong
(对应的 C 类型)。C 中的 uintptr_t
与 jlong
是兼容的。
在 Linux 上,我的 uintptr_t
长度为 8 字节,因为我处于 64 位应用程序的 amd64 环境中。而对于 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 = (jlong) connection;
jobject instance = (*env)->NewObject(env, ps->class, constructor, jconnection, jusername);
将类型正确地进行简单强制转换是解决方法。我预计这个问题在 CallVoidMethod
或文档中提到的任何 Call*Method
中也存在。
英文:
After days, I figured it out.
The constructor I am invoking expects a parameter. The type is long
(Java) aka J
(JNI Type Signature) aka jlong
(corresponing C type). A C uintptr_t
is compatible with a jlong
.
On Linux, my uintptr_t
is 8 bytes long, since I am in a amd64 environment with 64 bit applications. For Windows, the application was build in 32 bit. As a result uintptr_t
is only 4 bytes long, but the JVM still expect a 8 byte jlong
. However, NewObject
is a variadic function, automatic promotion does not happen and type safety is not guaranteed.
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);
A simple cast to the correct type was the solution. I expect this pitfall to exist with CallVoidMethod
or any of the Call*Method
mentioned in the documentation, too.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论