你可以在使用JNI从java调用的c ++函数中创建一个新的JVM吗?

时间:2016-03-08 14:42:01

标签: java c++ c dll jvm

所以我的设置是我有一个由我开发的.dll( A.dll ),它在原始应用程序中从外部进程调用,该进程基本上只是一个.exe文件我没有( B.exe )的源代码。 A.dll 的目的是与.jar文件进行通信,该文件也是由我开发的( C.jar )。所以在应用程序中,“通信流程”如下所示

B.exe - > A.dll - > (通过JNI) - > C.jar

现在,我想要做的是在开发环境中添加 A.dll C.jar 之间的调用作为我的测试套件的一部分的 C.jar 即可。到目前为止,我已经创建了另一个.dll( D.dll ),它反映了 A.dll 中的所有功能,但是使用了JNIEXPORT,只是直接调用到 A.dll 中的相应功能。所以这种情况下的“沟通流程”如下:

C.jar开发框架中的单元测试 - > (通过JNI) - > D.dll - > A.dll - > (通过JNI) - >的 C.jar

此时,一个非常简单的函数调用只是在 C.jar 中打印出来的东西在整个链中起作用;从单元测试电话一直到 C.jar 。但是当我在 A.dll 中调用函数时会出现问题,该函数使用 CreateJavaVM()创建一个新的JVM,这会产生以下错误:

在VM初始化期间发生错误 无法加载本机库:找不到指定的过程

所以基本上我想知道它是否真的可以这样做,或者只是在同一进程中已经有一个正在运行的JVM时,只是不可能调用 CreateJavaVM()?我知道你不能在同一个进程中多次调用 CreateJavaVM(),但是在这种情况下它只调用一次但是过程中已经存在JVM - 你甚至可以运行几个JVM在同一个过程中?

SOLUTION:

感谢@ apangin的回答,下面的代码片段解决了我的问题:

jsize nVMs = 0; 
JavaVM** buffer;

jni_GetCreatedJavaVMs = (GetCreatedJavaVMs) GetProcAddress(GetModuleHandle(
  TEXT("jvm.dll")), "JNI_GetCreatedJavaVMs");

if (jni_GetCreatedJavaVMs == NULL) {
  // stuff
  CreateJavaVM(&jvm, (void **) &env, &args);
} else {
  jni_GetCreatedJavaVMs(NULL, 0, &nVMs); // 1. just get the required array   length
  JavaVM** buffer = new JavaVM*[nVMs];
  jni_GetCreatedJavaVMs(buffer, nVMs, &nVMs); // 2. get the data
  buffer[0]->GetEnv((void **) &env, jni_version); // 3. get environment
  jvm = buffer[0];
}

1 个答案:

答案 0 :(得分:5)

当前的JNI规范明确指出 creation of multiple VMs in a single process is not supported ,这实际上是在HotSpot source code中声明的。

即使你的dll只调用JNI_CreateJavaVM一次,也不意味着这是整个过程中的第一次调用。事实上,JNI_CreateJavaVM首先由java.exe或IDE的另一个启动器(idea.exeeclipse.exenetbeans.exe等)调用。

因此,A.dll不应盲目地创建Java VM,而应首先通过调用JNI_GetCreatedJavaVMs检查当前进程中JVM是否已存在。如果函数返回非空数组,则使用GetEnvAttachCurrentThread获取现有VM的JNIEnv*,否则创建新VM。