您可以使用JNI在从java调用的c++函数中创建一个新的JVM吗?[英] Can you create a new JVM in a c++ function called from java using JNI?

问题描述

所以我的设置是我有一个由我开发的 .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在同一个过程中?

解决方案:

感谢@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];
}

推荐答案

当前的 JNI 规范明确指出 不支持在单个进程中创建多个 VM,这实际上是在 HotSpot源代码.p>

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

因此,A.dll 应该首先通过调用 JNI_GetCreatedJavaVMs.如果函数返回非空数组,则使用 GetEnvAttachCurrentThread 为现有 VM 获取 JNIEnv*,否则创建一个新 VM.

本文地址:https://www.itbaoku.cn/post/978402.html