JavaVM *javaVM;
JNIEnv *env;
// VM initialization arguments
JavaVMInitArgs vmArgs;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-XX:+NativeMemoryTracking=detail";
vmArgs.version = JNI_VERSION_1_8;
vmArgs.nOptions = 1;
vmArgs.options = options;
vmArgs.ignoreUnrecognized = false;
// Create Java VM
JNI_CreateJavaVM(&javaVM, (void**)&env, &vmArgs);
delete options;
Compile code with the following:
JAVA_INCLUDES=-I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/$(JAVA_OS)/ -L$(JAVA_HOME)/jre/lib/amd64/server
g++ jnicode.cpp $(JAVA_INCLUDES) -ljvm
When this program is compiled using the above compile command, and executed, it reports the following error:
Java HotSpot(TM) 64-Bit Server VM warning: Native Memory Tracking did not setup properly, using wrong launcher?
The reason for this behavior is that the Native Memory Tracker requires an environment variable to be set for the process before the JVM can be initialized and created. That environment variable is NMT_LEVEL_<pid> where <pid> is the identifier of the process. Also, please note that since this environment variable should already be set before the JVM gets initialized, we need to dynamically load the JVM shared library at runtime after setting the env variable .
Here's the sample code showing how we can do that:
// env variable to be set for NMT
const char* NMT_Env_Name = "NMT_LEVEL_";
const int TOTAL_LEN = strlen(NMT_Env_Name)+20;
char *nmtEnv = (char*)calloc(TOTAL_LEN,1);
snprintf(nmtEnv,TOTAL_LEN, "%s%d", NMT_Env_Name, getpid());
// Set the env variable
setenv(nmtEnv, "detail", 1);
// Dynamically load libjvm.so
const char* jvmpath = "<path to libjvm.so>/libjvm.so";
void* libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
if (libjvm == NULL) {
printf("Could not open libjvm.so\n");
return -1;
}
// VM initialization arguments
JavaVMInitArgs vmArgs;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-XX:+NativeMemoryTracking=detail";
vmArgs.version = JNI_VERSION_1_8;
vmArgs.nOptions = 1;
vmArgs.options = options;
vmArgs.ignoreUnrecognized = false;
// get a handle to JNI_CreateJavaVM function in the dynamically loaded JVM
CreateJavaVM_t CreateJavaVM;
CreateJavaVM = (CreateJavaVM_t)dlsym(libjvm, "JNI_CreateJavaVM");
// Create the JVM
JavaVM *javaVM;
JNIEnv *jniEnv;
long flag = CreateJavaVM(&javaVM, (void**)&jniEnv, &vmArgs);
Compile code with the following:
g++ jnivm.cc $(JAVA_INCLUDES) -ldl
In the above example code, I am setting env variable NMT_LEVEL_<pid> and dynamically loading libjvm.so after setting this env variable. Note that I removed -ljvm from the g++ options so as not to load the JVM shared library at the process load time. With these simple changes, NMT can be used with the custom JVM launchers.
Thanks Poonam, I had teased this out from talking with Support.
There is one more tricky thing this code doesn't cover, and that's -Xss.
To set the stack size correctly you need to create a new thread using pthreads and set it's stack size before you start it, and then create the JVM from the new thread. :)
I've got buildable code for both here:
https://github.com/areese/launch_jvm_from_jni
Thanks Allen!