将 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++ 应用程序:如何正确链接?的主要内容,如果未能解决你的问题,请参考以下文章

没有 JVM 的 Java

将 mono 与嵌入它的 c++ 应用程序捆绑在一起

将窗口(glfwCreateWindow)作为子嵌入到 C++ MFC 父窗体

如何将非托管 C++ 表单嵌入到 .NET 应用程序中?

如何将多媒体 HTML/JavaScript 应用程序嵌入到 C++ 代码中

如何在适用于 Android 和 iOS 的 C++ 跨平台库中正确链接 OpenCV?