The following code succeeds most of the time, but sometimes causes the program to crash (all threads exiting with code 1).
string classpath;
JavaVMOption options[6];
std::string heapOptionString = "-Xms500m"; //Important !!! please adjust when needed, default is 1GB of RAM usage = "-Xms1g"
std::string jmxOptionString = "-Dcom.sun.management.jmxremote";
std::string unlockCFOptionString = "-XX:+UnlockCommercialFeatures";
std::string flightRecOptionString = "-XX:+FlightRecorder";
std::string useGC1 = "-XX:+UseG1GC";
options[0].optionString = new char[classpath.size()+1];
strcpy_s(options[0].optionString, classpath.size()+1,classpath.c_str());
options[1].optionString = new char[heapOptionString.size()+1];
strcpy_s(options[1].optionString, heapOptionString.size()+1,heapOptionString.c_str());
options[2].optionString = new char[jmxOptionString.size()+1];
strcpy_s(options[2].optionString, jmxOptionString.size()+1, jmxOptionString.c_str());
options[3].optionString = new char[unlockCFOptionString.size()+1];
strcpy_s(options[3].optionString, unlockCFOptionString.size()+1, unlockCFOptionString.c_str());
options[4].optionString = new char[flightRecOptionString.size()+1];
strcpy_s(options[4].optionString, flightRecOptionString.size()+1, flightRecOptionString.c_str());
options[5].optionString = new char[useGC1.size()+1];
strcpy_s(options[5].optionString, useGC1.size()+1, useGC1.c_str());
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 6;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
int ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
It is inconsistent - sometimes I am able to run my application multiple times during the day, but once the problem appears - it crashes every time and the only way to resolve it is to log off and log in again (some java service hanging in memory?).
I've checked other questions on SO, relating to the creation of VM trough JNI, but they seem to be related to other causes (old bugs in Java 1.6 that are supposed to be fixed now) or have different circumstances - i.e. 100% reproducible crash.
EDIT: I should also mention, that this code is part of a method in a class, that is essentially a wrapper over a JAVA library that I am using. Also it is executed from non-main thread and there is no prior calls to JNI whatsoever.
Related
I am trying to run a java program using c++ code. I tried below code
#include<jni.h>
#include<stdio.h>
int main(int argc, char** argv) {
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 0;
vm_args.ignoreUnrecognized = 1;
// Construct a VM
jint results = JNI_CreateJavaVM(&vm, (void**)& env, &vm_args);
// Construct a String
jstring jstr = env->NewStringUTF("Hello World");
// First get the class that contains the method you need to call
jclass clazz = env->FindClass("java/lang/String");
// Get the method that you want to call
jmethodID to_lower = env->GetMethodID(clazz, "toLowerCase",
"()Ljava/lang/String;");
// Call the method on the object
jobject result = env->CallObjectMethod(jstr, to_lower);
// Get a C-style string
const char* str = env->GetStringUTFChars((jstring)result, NULL);
printf("%s\n", str);
// Clean up
env->ReleaseStringUTFChars(jstr, str);
// Shutdown the VM.
vm->DestroyJavaVM();
}
I used below command to compile the code
g++ LoadJVM.c -I/c/apps64/Java/jdk-11.0.1/include -I/c/apps64/Java/jdk-11.0.1/include/win32 -L/c/apps64/Java/jdk-11.0.1/lib/ -ljvm
It compiles fine, but when i run the executable like below, i am facing error
./a.exe
Error
error while loading shared libraries: jvm.dll: cannot open shared object file: No such file or directory
Any Idea why this jvm.dll is not getting loaded?
PS: I am compiling and running from Git-Bash on windows 10.
It looks like your jvm.dll can not be found.
Let's say we have 32bit MinGW installation (this is the version I have).
simple.cc
#include<jni.h>
#include<stdio.h>
int main(int argc, char** argv) {
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 0;
vm_args.ignoreUnrecognized = 1;
// Construct a VM
jint results = JNI_CreateJavaVM(&vm, (void**)& env, &vm_args);
printf("Hello");
// Shutdown the VM.
(*vm).DestroyJavaVM();
}
compilation and execution
> export JAVA_HOME="/c/Program\ Files\ \(x86\)/Java/jdk1.8.0_211/"
> export PATH="/c/Program Files (x86)/Java/jdk1.8.0_211/jre/bin/server/":"$PATH"
> g++ -o simple simple.cc -I"$JAVA_HOME/include/" -I"$JAVA_HOME/include/win32/" -L"$JAVA_HOME/lib" -ljvm
> ./simple
Hello
you have to make sure that jvm.dll is visible on %PATH% - $PATH inside git-bash.
So i need to use jni to call java method.
i started with this code :
JNIEnv *env1;
JavaVM** jvm1;
JavaVMInitArgs vm_args1;
JavaVMOption options1[1];
options1[0].optionString = "-Djava.class.path=D:\\Java Src\\TestStruct"; //Path to the java source code
vm_args1.version = JNI_VERSION_1_6; //JDK version. This indicates version 1.6
vm_args1.nOptions = 1;
vm_args1.options = options1;
vm_args1.ignoreUnrecognized = 0;
int reAt = JNI_CreateJavaVM(jvm1, (void**)&env1, &vm_args1);
if(reAt < 0)
Label1->Caption = "failed";
else
Label1->Caption = "Success";
I convert jvm.lib to OMF lib for to use in my builder C++ Application :
COFF2OMF jvm.lib jvm2.lib
I added jvm2.lib to the library path to fixe link error about JNI_CreateJavaVM function.
Now my application compile without error.
But it crash when it call JNI_CreateJavaVM function.
I added the jvm.dll near my .exe
What is the pb in there steps??
How can fixe it?
Thx
The first argument to JNI_CreateJavaVM should be a valid pointer to JavaVM*:
JavaVM* jvm1;
int reAt = JNI_CreateJavaVM(&jvm1, ...);
In your example, jvm1 is uninitialized.
Consult the documentation for details.
I need to call some methods from a jar using JNI in c++.But i need to call it a lot of times in different instances of thesame class.
My JNI is something like this
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
jmethodID Sim_constr = NULL;
jmethodID Read_XML = NULL;
jmethodID configure = NULL;
jmethodID initial = NULL;
jmethodID results = NULL;
jint step = 60;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=<My jar>";
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
/* load and initialize a Java VM, return a JNI interface
* pointer in env */`
long status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (status == JNI_ERR){
exit(10);
}
//Some Java method Calls... `
I need to set once the JNI and then just call 3-4 methods OR to kill/delete completely the JNI parameters and then create it again.
I suppose you may try to accomplish the following
After creating a JVM using CreateJavaVM method your JVM remains Global for all instance of the class, the environment variable(env) should be local for each instance of the class. And also env var for each instance must be used to call your methods many times using different instances.
This snippet will get you the env from JVM:(place this such that every instance has local copy of env and global copy of jvm)
After creating the instance you have to attach these env to the current thread(i.e current instance)
JNIEnv * g_env;
int getEnvStat = jvm->GetEnv((void **)&g_env, JNI_VERSION_1_8);
if (getEnvStat == JNI_EDETACHED) {
printf("GetEnv: not attached");
getEnvStat = g_vm->AttachCurrentThread((void **) &g_env, NULL);
if ( getEnvStat != 0) {
printf("Failed to attach");
}
} else if (getEnvStat == JNI_OK) {
} else if (getEnvStat == JNI_EVERSION) {
printf("GetEnv: version not supported");
}
//use the g_env variable for java method calls
//Rememeber to use this code as a local copy for each instance
I want to load my own native libraries in my java application. Those native libraries depend upon third-party libraries (which may or may not be present when my application is installed on the client computer).
Inside my java application, I ask the user to specify the location of dependent libs. Once I have this information, I am using it to update the "LD_LIBRARY_PATH" environment variable using JNI code. The following is the code snippet that I am using to change the "LD_LIBRARY_PATH" environment variable.
Java code
public static final int setEnv(String key, String value) {
if (key == null) {
throw new NullPointerException("key cannot be null");
}
if (value == null) {
throw new NullPointerException("value cannot be null");
}
return nativeSetEnv(key, value);
}
public static final native int nativeSetEnv(String key, String value);
Jni code (C)
JNIEXPORT jint JNICALL Java_Test_nativeSetEnv(JNIEnv *env, jclass cls, jstring key, jstring value) {
const char *nativeKey = NULL;
const char *nativeValue = NULL;
nativeKey = (*env)->GetStringUTFChars(env, key, NULL);
nativeValue = (*env)->GetStringUTFChars(env, value, NULL);
int result = setenv(nativeKey, nativeValue, 1);
return (jint) result;
}
I also have corresponding native methods to fetch the environment variable.
I can successfully update the LD_LIBRARY_PATH (this assertion is based on the output of C routine getenv().
I am still not able to load my native library. The dependent third-party libraries are still not detected.
Any help/pointers are appreciated. I am using Linux 64 bit.
Edit:
I wrote a SSCE (in C) to test if dynamic loader is working. Here is the SSCE
#include
#include
#include
#include
int main(int argc, const char* const argv[]) {
const char* const dependentLibPath = "...:";
const char* const sharedLibrary = "...";
char *newLibPath = NULL;
char *originalLibPath = NULL;
int l1, l2, result;
void* handle = NULL;
originalLibPath = getenv("LD_LIBRARY_PATH");
fprintf(stdout,"\nOriginal library path =%s\n",originalLibPath);
l1 = strlen(originalLibPath);
l2 = strlen(dependentLibPath);
newLibPath = (char *)malloc((l1+l2)*sizeof(char));
strcpy(newLibPath,dependentLibPath);
strcat(newLibPath,originalLibPath);
fprintf(stdout,"\nNew library path =%s\n",newLibPath);
result = setenv("LD_LIBRARY_PATH", newLibPath, 1);
if(result!=0) {
fprintf(stderr,"\nEnvironment could not be updated\n");
exit(1);
}
newLibPath = getenv("LD_LIBRARY_PATH");
fprintf(stdout,"\nNew library path from the env =%s\n",newLibPath);
handle = dlopen(sharedLibrary, RTLD_NOW);
if(handle==NULL) {
fprintf(stderr,"\nCould not load the shared library: %s\n",dlerror());
exit(1);
}
fprintf(stdout,"\n The shared library was successfully loaded.\n");
result = dlclose(handle);
if(result!=0) {
fprintf(stderr,"\nCould not unload the shared library: %s\n",dlerror());
exit(1);
}
return 0;
}
The C code also does not work. Apparently, the dynamic loader is not rereading the LD_LIBRARY_PATH environment variable. I need to figure out how to force the dynamic loader to re-read the LD_LIBRARY_PATH environment variable.
See the accepted answer here:
Changing LD_LIBRARY_PATH at runtime for ctypes
In other words, what you're trying to do isn't possible. You'll need to launch a new process with an updated LD_LIBRARY_PATH (e.g., use ProcessBuilder and update environment() to concatenate the necessary directory)
This is a hack used to manipulate JVM's library path programmatically. NOTE: it relies on internals of ClassLoader implementation so it might not work on all JVMs/versions.
String currentPath = System.getProperty("java.library.path");
System.setProperty( "java.library.path", currentPath + ":/path/to/my/libs" );
// this forces JVM to reload "java.library.path" property
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
This code uses UNIX-style file path separators ('/') and library path separator (':'). For cross-platform way of doing this use System Properties to get system-specific separators: http://download.oracle.com/javase/tutorial/essential/environment/sysprop.html
I have successfully implemented something similar for CollabNet Subversion Edge, which depends on the SIGAR libraries across ALL Operating Systems (we support Windows/Linux/Sparc both 32 bits and 64 bits)...
Subversion Edge is a web application that helps one managing Subversion repositories through a web console and uses SIGAR to the SIGAR libraries helps us provide users data values directly from the OS... You need to update the value of the property "java.library.path" at runtime. (https://ctf.open.collab.net/integration/viewvc/viewvc.cgi/trunk/console/grails-app/services/com/collabnet/svnedge/console/OperatingSystemService.groovy?revision=1890&root=svnedge&system=exsy1005&view=markup Note that the URL is a Groovy code, but I have modified it to a Java here)...
The following example is the implementation in URL above... (On Windows, your user will be required to restart the machine if he/she has downloaded the libraries after or downloaded them using your application)... The "java.library.path" will update the user's path "usr_paths" instead of System path "sys_paths" (permissions exception might be raised depending on the OS when using the latter).
133/**
134 * Updates the java.library.path at run-time.
135 * #param libraryDirPath
136 */
137 public void addDirToJavaLibraryPathAtRuntime(String libraryDirPath)
138 throws Exception {
139 try {
140 Field field = ClassLoader.class.getDeclaredField("usr_paths");
141 field.setAccessible(true);
142 String[] paths = (String[])field.get(null);
143 for (int i = 0; i < paths.length; i++) {
144 if (libraryDirPath.equals(paths[i])) {
145 return;
146 }
147 }
148 String[] tmp = new String[paths.length+1];
149 System.arraycopy(paths,0,tmp,0,paths.length);
150 tmp[paths.length] = libraryDirPath;
151 field.set(null,tmp);
152 String javaLib = "java.library.path";
153 System.setProperty(javaLib, System.getProperty(javaLib) +
154 File.pathSeparator + libraryDirPath);
155
156 } catch (IllegalAccessException e) {
157 throw new IOException("Failed to get permissions to set " +
158 "library path to " + libraryDirPath);
159 } catch (NoSuchFieldException e) {
160 throw new IOException("Failed to get field handle to set " +
161 "library path to " + libraryDirPath);
162 }
163 }
The Bootstrap services (Groovy on Grails application) class of the console runs a service and executes it with the full path to the library directory... UNiX-based servers do not need to restart the server to get the libraries, but Windows boxes do need a server restart after the installation. In your case, you would be calling this as follows:
String appHomePath = "/YOUR/PATH/HERE/TO/YOUR/LIBRARY/DIRECTORY";
String yourLib = new File(appHomePath, "SUBDIRECTORY/").getCanonicalPath();
124 try {
125 addDirToJavaLibraryPathAtRuntime(yourLib);
126 } catch (Exception e) {
127 log.error("Error adding the MY Libraries at " + yourLib + " " +
128 "java.library.path: " + e.message);
129 }
For each OS you ship your application, just make sure to provide a matching version of the libraries for the specific platform (32bit-Linux, 64bit-Windows, etc...).
I'm making a falling sand game in Java. I want users to be able to write their own engine for it using a simpler language. Falling sand games can be very CPU intensive so I want to have the engine running as fast as possible while not having to manually compile.
I need to know how to compile rhino javascript files to .class files by at runtime to be used.
I've looked for a way but couldn't find any other than manually compiling it by using the command line which I don't want users to have to do.
There's a short tutorial here:
Scripting: Compiling Scripts in Java
My solution here:
Has anyone used or written an Ant task to compile (Rhino) JavaScript to Java bytecode?
You can compile your scripts at runtime using Context.compileString(). This produces a Script object which you can reuse.
Script s = someContext.compileString(myScript, "<cmd>", 1, null);
// Store s, cache it in a map or something, maybe even serialize and persist it.
// Later...
Object result = s.exec(anotherContext, someScope);
The performance difference between something like this and using Context.evaluateString() could easily be multiple orders of magnitude faster.
You can try the follow sample:
void toClassFile( String script ) throws IOException {
CompilerEnvirons compilerEnv = new CompilerEnvirons();
ClassCompiler compiler = new ClassCompiler( compilerEnv );
Object[] compiled = compiler.compileToClassFiles( script, null, 1, "javascript.Test" );
for( int j = 0; j != compiled.length; j += 2 ) {
String className = (String)compiled[j];
byte[] bytes = (byte[])compiled[(j + 1)];
File file = new File( className.replace( '.', '/' ) + ".class" );
file.getParentFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream( file )) {
fos.write( bytes );
}
}
}