异常 0xC0000005 在 jvm.dll 中,在通过 JNI 的 NewObject 创建实例时发生。

huangapple go评论163阅读模式
英文:

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)-&gt;GetMethodID(env, ps-&gt;class, &quot;&lt;init&gt;&quot;, &quot;(JLjava/lang/String;)V&quot;);
    jstring jusername = (*env)-&gt;NewStringUTF(env, username);
    jobject instance = (*env)-&gt;NewObject(env, ps-&gt;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_tjlong 是兼容的。

在 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)-&gt;GetMethodID(env, ps-&gt;class, &quot;&lt;init&gt;&quot;, &quot;(JLjava/lang/String;)V&quot;);
    jstring jusername = (*env)-&gt;NewStringUTF(env, username);
    jlong jconnection = connection;
    jobject instance = (*env)-&gt;NewObject(env, ps-&gt;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.

huangapple
  • 本文由 发表于 2020年3月15日 07:06:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/60688140.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定