您可以在使用 JNI 从 java 调用的 c++ 函数中创建一个新的 JVM 吗?
Posted
技术标签:
【中文标题】您可以在使用 JNI 从 java 调用的 c++ 函数中创建一个新的 JVM 吗?【英文标题】:Can you create a new JVM in a c++ function called from java using JNI? 【发布时间】:2016-03-08 14:42:00 【问题描述】:所以我的设置是我有一个由我开发的 .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 的回答,下面的代码 sn-p 解决了我的问题:
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];
【问题讨论】:
很好的问题,但我会保持每一层的责任,而不是紧密耦合。 C.jar 不需要了解有关客户端的任何信息,架构更改对于修复来说可能很痛苦。我会保留 C.jar 的单元测试只是为了它自己的方法,然后 A.dll 测试将通过它自己和 JNI 结果运行。然后放入一些持续集成服务器。是否可以通过 a.dll 测试 c.jar 结果? 但是由于JNI接口,A.dll和C.jar的耦合已经很紧密了。我已经有一个单独的 A.dll 测试套件,它调用 C.jar,但我觉得如果我可以将所有测试放在一个地方会更好,因为 A.dll 和 C.jar 之间的耦合是反正这么紧。 【参考方案1】:当前的 JNI 规范明确声明 creation of multiple VMs in a single process is not supported,这实际上是在 HotSpot source code 中声明的。
即使你的 dll 只调用了一次JNI_CreateJavaVM
,也不意味着这是整个过程中的第一次调用。事实上,JNI_CreateJavaVM
首先由java.exe
或您的IDE 的另一个启动器(idea.exe
、eclipse.exe
、netbeans.exe
等)调用。
因此,A.dll
应该首先通过调用JNI_GetCreatedJavaVMs 来检查当前进程中是否已经存在 JVM,而不是盲目地创建 Java VM。如果函数返回非空数组,则使用GetEnv或AttachCurrentThread为现有VM获取JNIEnv*
,否则创建一个新VM。
【讨论】:
感谢您的回复,目前正在测试中 如果没有虚拟机存在,JNI_GetCreatedJavaVMs 似乎会终止整个过程......对此有什么想法吗? 哦,我想通了——真是愚蠢的错误。所以我得到了这样的 JNI_GetCreatedJavaVMs 函数:jni_GetCreatedJavaVMs = (GetCreatedJavaVMs) GetProcAddress(GetModuleHandle( TEXT("jvm.dll")), "JNI_GetCreatedJavaVMs");
,我忘了在调用jni_GetCreatedJavaVMs(NULL, 0, &nVMs);
之前检查jni_GetCreatedJavaVMs
是否为NULL
由于声明 C++ 代码是从 Java 调用的,JNI_GetCreatedJavaVMs()
不可能不返回任何 JVM。很难看出这个问题或所谓的解决方案是关于什么的,
@EJP 请在投票前仔细阅读问题。它明确指出A.dll
可以从外部(非Java)应用程序B.exe
或从Java 框架C.jar
调用。以上是关于您可以在使用 JNI 从 java 调用的 c++ 函数中创建一个新的 JVM 吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 JNI 从 JAVA 调用带有 C++ 参数的函数?