将 JVM 嵌入 C++ 应用程序:如何正确链接?
Posted
技术标签:
【中文标题】将 JVM 嵌入 C++ 应用程序:如何正确链接?【英文标题】:Embedding JVM into C++ application: How to link it properly? 【发布时间】:2020-08-03 02:49:51 【问题描述】:目标
比方说,我们有一个大型 C++ 应用程序,它可以做一些大事情,这些事情最好用 C++ 来完成。但是,有很多逻辑,我们更愿意在 JVM 上运行。
问题
为了测试上述方法,我使用 CLion 创建了small C++ project。 场景是:
构建(由 CMake 自动生成):
链接代码sn-p 与$JAVA_HOME/lib/jvm.lib
将$JAVA_HOME
中的所有DLL复制到刚刚创建的exe
目录下
运行:
创建嵌入式 JVM 调用此 JVM 托管的静态方法我尝试了 Visual Studio 2019 Community 和 MinGW64 (x86_64-8.1.0-posix-seh-rt_v6-rev0) 工具链,都得到了相同的结果。
我使用 OpenJDK 获得的最佳结果 (jdk-14.0.2):
Error occurred during initialization of VM
Failed setting boot class path.
Oracle JDK 1.8 出现了一些不同的故障:
Error occurred during initialization of VM
Unable to load native library: Can't find dependent libraries
我还尝试了 ojdkbuild 的不同版本,我得到的最好的版本是 env->FindClass
调用中的 SEGFAULT。
问题
-
哪种 JVM 发行版更适合嵌入到 C++ 中?
如何正确链接和分发?
【问题讨论】:
您不能将两者作为单独的进程运行,并让 c++ 代码通过正常的进程间通信策略(例如 REST Web 服务、共享内存、同步原语等)调用 Java 代码吗?跨度> 可能重复:***.com/questions/7537822/… @MarcioLucca 是的,进程间通信是一个很好的解决方案,适用于大多数情况并且易于实现。但这对我的情况来说还不够好,我对在同一进程中运行 JVM 很感兴趣。 @OrenIshShalom 这不是重复的。您链接中问题的作者询问“如何更快地驾驶汽车”,但我的问题是“如何启动汽车的发动机”。但是感谢您的链接 - 在寻求解决方案时我没有看到它 - 它非常相关,也许有一些提示。 我知道,我知道。这并不完全是您正在寻找的东西(就像 macOS 一样),但我很确定您可以使用类似的方法。该视频处理完全相同的情况 - 将 JVM 嵌入您的应用程序包中。看看名为jlink
的东西 - 它可能对你有用:youtu.be/-DwcxuWXIgI
【参考方案1】:
Windows 解决方案:
-
编写 C++ 代码:
-
包括来自 JDK 的
<jni.h>
。
使用来自 WinAPI 的 LoadLibrary
加载“$JRE_HOME/bin/server/jvm.dll”。
使用来自 WinAPI 的 GetProcAddress
获取指向 JNI_CreateJavaVM
的指针。
调用JNI_CreateJavaVM
并使用JVM 为所欲为。
$JAVA_HOME/include/**/*.h
。
只需运行编译好的程序。
这里的工作示例:https://bitbucket.org/kkolyan/j4cpp/src/master/
【讨论】:
我在调试一些非常相似的代码时找到了这个答案。我正在尝试在 Unity 中加载 JVM。我看到你也在做同样的事情。不幸的是,您的示例和我自己的代码都无法正常工作。在实例化 JVM 时,两者似乎都产生了这个错误:在执行本机代码时得到了一个 SIGSEGV。想法? 您在第二次游戏运行时收到了此 SIGSEGV。对?发生这种情况是因为每个进程只能启动一次 JVM(任何已知 JVM 的限制)。对于 Unity Editor,这意味着每次运行的游戏都应该在创建之前先尝试加入现有的 JVM。永远不要手动销毁它。 PS:是的,我用 Unity+Java 进行了实验,并编写了 simple library 以通过 C# 以反射风格与 JVM 一起工作。【参考方案2】:哪种 JVM 发行版更适合嵌入到 C++ 中?
嵌入没有区别,因为其中很大一部分只是带有小调整的 OpenJDK 代码。
如何正确链接和分发?
将 $JAVA_HOME 中的所有 DLL 复制到刚刚创建的 exe 所在目录
这行不通,因为 JVM 需要更多。
您将不得不分发 JDK 的一部分,仅 DLL 是不够的,因为所有类库都丢失了。您可以尝试使用jimage
构建较小的图像。这将包括所有相关部分(=JVM、类库、本机库)
要链接到 JVM,请以正确的方式进行。
【讨论】:
谢谢!可以将整个 JRE 与应用程序一起携带。 [1] 确保所有必需的东西(类库等)对嵌入式 JVM 可见的正确方法是什么? [2] 我是否只需要在我的分布中保留所有 JRE 文件之间的原始相对文件位置? [3] 然后看来我不能只将 DLL 与我的 EXE 放在同一个文件夹中(至少因为 JVM 的 DLL 位于不同的文件夹中)。所以我只能使用特定于平台的方式(如LoadLibrary
)手动加载 DLL? [4] 我的 EXE 和 JRE 之间的相对路径是什么?
[1] - 如上所述作为答案。 [2]、[3] - 是的。 [4] - 不,您可以从任何位置加载 JRE。以上是关于将 JVM 嵌入 C++ 应用程序:如何正确链接?的主要内容,如果未能解决你的问题,请参考以下文章
将窗口(glfwCreateWindow)作为子嵌入到 C++ MFC 父窗体