2

Goal

Let's say, we have a large C++ application that does some large things, that are good to be done using C++. But, there are a plenty of logic, that we'd prefer to run on JVM.

Issue

To test approach described above, I created small C++ project using CLion. Scenario is:

Build (automated by CMake):

  • link code snippet with $JAVA_HOME/lib/jvm.lib
  • copy all DLLs from $JAVA_HOME to the directory with just created exe

Run:

  • create embedded JVM
  • invoke static method hosted by this JVM

I tried Visual Studio 2019 Community and MinGW64 (x86_64-8.1.0-posix-seh-rt_v6-rev0) toolchains and both led to identical results.

The best results I've got with OpenJDK (jdk-14.0.2):

Error occurred during initialization of VM
Failed setting boot class path.

Oracle JDK 1.8 has shown a bit different failure:

Error occurred during initialization of VM
Unable to load native library: Can't find dependent libraries

I also tried different versions from ojdkbuild and the best I get is SEGFAULT at the env->FindClass invocation.

Questions

  1. What distribution of JVM is better to use for embedding to C++?
  2. How to link and distribute it all properly?
kkolyan
  • 125
  • 1
  • 10
  • Can't you run the two as separate processes and have the c++ code invoke the Java code via normal inter process communication strategies (e.g. REST web services, shared memory, synchronization primitives, etc)? – Marcio Lucca Aug 03 '20 at 03:56
  • possible duplicate: https://stackoverflow.com/questions/7537822/looking-for-a-convenient-way-to-call-java-from-c – OrenIshShalom Aug 03 '20 at 04:29
  • @MarcioLucca Yes, inter process communication is a good solution suitable for most of cases and easy to implement. But it's not good enough for my case and I'm interesting in running JVM in the same process. – kkolyan Aug 03 '20 at 09:33
  • @OrenIshShalom It's not a duplicate. Author of the question from your link asks "how to drive a car faster", but my question is "how to start engine of the car". But thanks for the link - I haven't seen it when sought for solution - it's pretty relevant and maybe there are some hints. – kkolyan Aug 03 '20 at 09:41
  • I know, I know. This is not exactly what you are looking for (as it is for macOS), but I am pretty sure you can use similar approach. This video treats exactly the same case - embedding JVM inside your application bundle. Take a look at something called `jlink` - it might be of use for you: https://youtu.be/-DwcxuWXIgI – Oo.oO Aug 03 '20 at 12:56
  • I think you're doing this the wrong way around: you want a Java project with native parts, shipped as java application. Using JNI or JNA for the C++ bridge. That's fine even if the native code outweighs the java code – Dracam Aug 04 '20 at 18:41
  • @Dracam I used JNA and JNA to call C code from Java in another projects. But for current case such approach is not suitable, because I want to embed Java into the existing applciation, that may be extended by C++ code, but cannot be used as a library. – kkolyan Aug 05 '20 at 12:51

2 Answers2

3

Solution for Windows:

  1. write C++ code that:
    1. includes <jni.h> from JDK.
    2. loads "${JRE_HOME}/bin/server/jvm.dll" using LoadLibrary from WinAPI.
    3. gets pointers to JNI_CreateJavaVM using GetProcAddress from WinAPI.
    4. calls JNI_CreateJavaVM and does whatever you want with JVM.
  2. compile it with access to ${JAVA_HOME}/include/**/*.h.
  3. just run compiled program.

Working example here: https://bitbucket.org/kkolyan/j4cpp/src/master/

kkolyan
  • 125
  • 1
  • 10
  • I found this answer while debugging some very similar code. I am trying to load the JVM in Unity. I see you are doing the same. Unfortunately both your example and my own code fail to work. Both seem to create this error when instantiating the JVM: Got a SIGSEGV while executing native code. Thoughts? – Marcel Offermans Feb 28 '21 at 12:38
  • 1
    You've received this SIGSEGV at the *second* run of game. Right? That happens because you may start JVM only once per process (limitation of any known JVMs). For Unity Editor it means that every game run should first try to join existing JVM before create. And never destroy it manually. PS: yeah, I experimented with Unity+Java and have written [simple library](https://bitbucket.org/kkolyan/nestedjvm-cs/src/master/Jni4CSharp/) to work with JVM from C# in reflection style. – kkolyan Mar 01 '21 at 16:46
1

What distribution of JVM is better to use for embedding to C++?

There is no difference in embedding, as a big part of them is just OpenJDK code with small tweaks.

How to link and distribute it all properly?

copy all DLLs from $JAVA_HOME to the directory with just created exe

This won't work, as the JVM needs a lot more.

You will have to distribute a part of the JDK, only the DLLs is not enough, as all class libraries are missing. You could try building a smaller image with jimage. This will include all relevant parts (=JVM,Classlibraries,native libraries)

To link against the JVM, you do it the right way.

JCWasmx86
  • 3,473
  • 2
  • 11
  • 29
  • Thanks! It's ok to carry whole JRE with application. **[1]** What is the correct way to ensure all required things (classlibraries, etc) are visible by emedded JVM? **[2]** Do I need just to preserve original relative file locations between all JRE files inside my distibution? **[3]** Then it seems I can't just put DLLs in the same folder with my EXE (at least because JVM's DLLs are living in different folders). So I can only to load manually DLLs using platform-specific way like `LoadLibrary`? **[4]** Does it make sense them what is the relative path between my EXE and JRE? – kkolyan Aug 06 '20 at 00:31
  • **[1]** - described above as an anwer. **[2]**, **[3]** - Yes. **[4]** - No, you can load JRE from any location. – kkolyan Aug 06 '20 at 03:20