How to load the Java virtual machine with C
Why write a C program to load a Java virtual machine?
Normally, after we complete the development of Java classes, we call Java.exe or Javaw.exe through scripts to launch a Java virtual machine, but in some cases, we need to load Java with C programs. Virtual machine runs our Java program, for example:
1, when running on Windows, you don't want the annoying DOS window, the DOS window is in addition to ugly, for the server program, sometimes this DOS window is dangerous, such as an administrator accidentally uses a mouse to use the mouse to use the mouse points. DOS window. The fork, or to copy what Ctrl-C is pressed, and will terminate the entire Java program.
2, you want to record the content displayed in the DOS window to the file so you can analyze it later. This is a very useful feature. Usually a lot of abnormal stacks, and the abnormal news that happen to the system are output in the DOS window. Sometimes the output content is too much, the content of the DOS window is not seen before, the DOS window is turned off, you can't see it again. Previously displayed content.
3, under Windows, you want to make the Java program into a background service, just like Tomcat, you can let the Windows operating system help you manage the Java program startup and stop.
4, you are developing a program like IE, only starting a virtual machine running a Java program when necessary
This article teaches you how to do this.
Two methods of loading virtual machines
Loading the virtual machine can take the following two methods:
1. Call Java.exe with the method of creating a sub-process, you can use the CreateProcess method under Windows, under Linux, use the Execvp method, the call method and the script are not more than that, this method is more simple, I will not introduce In detail, you can refer to Open Source Project Java Service Wrapper. Use the following advantages to create a sub-process:
1) Simple development, good compatibility, is the same for different versions of the Java virtual machine
2) The created Java process and the process of the C program are separated, and you will not interact, you will not have a Java process, if you have any memory error crashes, causing the Java process (of course, the separation process has brought other questions, see me Later analysis).
But I don't like this method because of the following shortcomings:
1) After generating separate Java processes, you will see another Java.exe process in the system. When you start a lot of such processes, you will see a lot of programs that are Java, which one you cannot distinguish.
2) After generating separate Java processes, you are not good for it. For example, if you want to turn off it, if you go directly to the Java process, you may have something you need to do in the Java process, you can't do it. If you want the Java program to perform the cleanup work and then exit, you need to build communication mechanisms between Java programs to send an exit message to the Java program, which usually needs to load specialized classes in the Java program, and need Java programs Developers perform related interface development.
2. Another way to load the Java virtual machine is to load the dynamic library of the JVM and start the Java virtual machine within this process through the interface of the dynamic library. I prefer this method, because this method can bring a lot of benefits:
1) You can better control the Java virtual machine, Java programs do not need to load specialized class libraries, you only need to call different Java class methods to control the startup of the Java program, stop, etc. Generate additional Java processes, the process name in the system process is the name of your C main program, you can clearly distinguish every process in the system process list
Of course, this method also has a shortcoming, but I think it is not a big problem:
1) Since the Java virtual machine is loaded inside the process, if the main program is not writing, the crash will cause the Java program to terminate together.
2) The dynamic library interface has to develop a little difficult, you need to know the interface of the JVM dynamic library, but you will see it later, this is not very difficult.
3) The interface of the JDK different versions is slightly different, and it may also change, you may have to modify the C program for different JDK versions, but relatively JVM compatibility should be trusted.
Method of loading a JVM dynamic library
Let's take a detailed introduction to the method of loading a dynamic library with a C program, I will introduce the Windows platform and the Linux / UNIX platform.
Loading method of Windows platform
The JDK Java Virtual Matter Library under the Windows Platform is JVM.DLL, located in:
% Java_home% / jre / bin / client / jvm.dll
% Java_Home% / JRE / BIN / Server / JVM.dll
The JVM dynamic library has two versions of Client and Server, which makes corresponding optimizations for desktop applications and server applications. The client version is relatively fast, and the server version is slower but run is faster.
Virtual machine loads can be made by following:
1) Load JVM dynamic library
2) Find jni_createjavaVM interface
3) Set the JVM startup parameter, call the JNI_CREATEJAVAVM interface to start the virtual machine
4) Find the startup class, set the command line parameters, set the window output redirective file
5) Start the Java program in the startup method of the startup class
6) To stop the Java program, call the Java class stop method
The following sample code illustrates a simple load process
TypedEf Jint (JNICALL * JNICREATEPROC) (javavm **, void **, void *);
Bool SetStream (JNIENV * ENV, Const Char * pszfilename, const char * pszmeth;
// Start Java virtual machine method
Bool StartJVM ()
{
// JVM dynamic library path
Const char szjvmpath [] = "c: //jdk1.5.0_01//jre//bin/server//jvm.dll";
// Java virtual machine startup parameters, each parameter written, can not be written together
INT NOPTIONCOUNT = 2;
JavaVmoption options [2];
Options [1] .Optionstring = "-xmx256m";
// Set ClassPath
Options [0] .Optionstring = "-djava.class.path =. / test.jar ;./ Test1.jar";
JavaVminitargs vm_args;
VM_ARGS.VERSION = JNI_Version_1_4;
VM_ARGS.OPTION = Options;
VM_ARGS.NOPTIONS = NOPTIONCOUNT; VM_ARGS.IGNOREUNRECognized = jni_true;
// Start the class, pay attention to the division is /, for example, start-up TEST.JTEST should be written as Test / Jtest
Const char szstartclass [] = "TEST / JTEST";
// Start method, usually the main function, you can also set it into other functions
Const char sstartmethod [] = "main";
// Heavy oriented file
Const char szstdoutfilename = "stdout.txt";
Const char szstderrfilename = "stderr.txt";
// Java program command line parameters
INT nParamcount = 2;
Const char * szparams [2] = {"arg1", "arg2"};
// Load JVM.
Hinstance JVMDLL = LoadLibrary (Szjvmpath);
IF (jvmdll == null)
{
Printf ("Load JVM Dynamic Library Error.% L", :: getLastError ());
Return False;
}
// Find the JNI_CReateJavAVM process.
JNICREATEPROC JVMCREATEPROC = (JNICREATEPROC) GetProcadDress (JVMDLL, "JNI_CREATEJAVAVAVM");
IF (JVMCREATEPROC == NULL)
{
Freelibrary (JVMDLL);
Printf ("Find the jni_createjavavm process error.% l", :: getLastError ());
Return False;
}
// Create JVM.
JNIENV * ENV;
JINT R = (JVMCREATEPROC) (& JVM, (void **) & env, & vm_args);
IF (r <0 || jvm == null || ENV == NULL)
{
Freelibrary (JVMDLL);
Printf ("Create a JVM error.");
Return False;
}
// Heavy directive stdout, stderr to output file
IF (! setStream (Env, SzstdoutFileName, "setout"))
{
Printf ("Setting the stdout output file failed");
Return False;
}
IF (! setStream (Env, SzstderrFileName, "Sterr"))
{
Printf ("Setting Stderr Output File Failed");
Return False;
}
// Load the startup class.
JCLASS ServiceClass = Env-> FindClass (SzstartClass);
IF (env-> exceptioncheck () == jni_true || serviceClass == null)
{
ENV-> EXCEPTIONDESCRIBE ();
ENV-> EXCEPTIONCLEAR ();
Freelibrary (JVMDLL);
Printf ("Load the startup class failed.");
Return False;
}
// Startup method
JMethodid Mid = Env-> getStaticMethodid (ServiceClass, SzstartMethod, "([Ljava / Lang / String;) V"); if (env-> exceptioncheck () == jni_true || MID == null)
{
ENV-> EXCEPTIONDESCRIBE ();
ENV-> EXCEPTIONCLEAR ();
Freelibrary (JVMDLL);
Printf ("Find the launch method failed.");
Return False;
}
/ / Find the String class.
JCLASS STRINGCLASS = ENV-> FindClass ("java / lang / string");
IF (env-> exceptioncheck () == jni_true || StringClass == NULL)
{
ENV-> EXCEPTIONDESCRIBE ();
ENV-> EXCEPTIONCLEAR ();
Freelibrary (JVMDLL);
Printf ("Find the String class failed.");
Return False;
}
JString JSTR;
For (int i = 0; i { Jstr = env-> newstringutf (Szparams [i]); IF (jstr == 0) { Printf ("Assign String Failed / N"); IF (env-> exceptionoCcurred ()) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); } Return False; } ENV-> SetObjectArrayElement (Args, I, JSTR); IF (env-> exceptioncheck () == jni_true) { Printf ("setting parameters failed / N"); IF (env-> exceptionoCcurred ()) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); } Return False; } } // Call the startup method to start the Java program Env-> CallStaticVoidMethod (ServiceClass, MID, ParameterArray); IF (env-> exceptioncheck () == jni_true) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); Freelibrary (JVMDLL); Return False; } Return True; } / / Method for setting output flow Bool SetStream (Jnienv * ENV, Const Char * PszFileName, Const Char * Pszmethod) { INT pBuffersize = 1024; Char * pbuffer = new char [pbuffersize]; // Create a string object. JString pathstring = env-> newstringutf (pszfilename); IF (env-> eXceptioncheck () == jni_true || pathstring == null) { ENV-> EXCEPTIONDESCRIBE (); env-> exceptionclear (); Printf ("Create a string failed."); Return False; } // Find the FileoutPutStream class. JCLASS FileOutputStreamClass = env-> FindClass ("Java / IO / FileoutputStream); IF (env-> exceptioncheck () == jni_true || fileOutputStreamClass == NULL) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); Printf ("Find the FileoutPutStream class failed."); Return False; } / / Find the FileoutPutStream class construction method. JMethodid FileOutputStreamConstructor = Env-> getMethodId (FileoutputStreamClass, " IF (env-> exceptioncheck () == jni_true || fileOutputStreamConstructor == null) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); Printf ("Find FileoutPutStream class construction method failed."); Return False; } // Create an object of the FileoutPutStream class. Jobject FileOutputStream = Env-> NewObject (FileoutPutStreamCstructor, PathString); IF (env-> exceptioncheck () == jni_true || fileOutputStream == NULL) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); Printf ("Create an object of the FileoutPutStream class failed."); Return False; } // Find the PrintStream class. JCLASS PRINTSTREAMCLASS = Env-> FindClass ("Java / IO / PrintStream"); IF (env-> Exceptioncheck () == jni_true || printstreamClass == NULL) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); Printf ("Find the PrintStream class failed."); Return False; } // Find the PrintStream class constructor. JMethodid PrintStreamConstructor = env-> getMethodid (PrintStreamClass, " IF (env-> exceptioncheck () == jni_true || PrintStreamConstructor == NULL) { ENV-> EXCEPTIONDESCRIBE (); env-> exceptionclear (); Printf ("Find the PrintStream class construction method failed."); Return False; } // Create an object of the PrintStream class. Jobject PrintStream = Env-> NewObject (PrintStreamClass, PrintStreamConstructor, FileoutputStream); IF (env-> exceptioncheck () == jni_true || printstream == NULL) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); Printf ("The object of creating a PrintStream class failed."); Return False; } / / Find the System class. JCLASS SystemClass = Env-> FindClass ("Java / Lang / System"); IF (env-> Exceptioncheck () == jni_true || systemclass == null) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); Printf ("Find the System class failed."); Return False; } / / Find the System class setting method. JMethodid SetStreamMethod = Env-> GetStaticMethodId (SystemClass, Pszmethod, "(Ljava / IO / PrintStream;) V"); IF (env-> exceptioncheck () == jni_true || setStreamMethod == NULL) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); Printf ("Find the System class setting method failed."); Return False; } // Set the stream of the System class. ENV-> CallStaticVoidMethod (SystemClass, SetStreamMethod, PrintStream); IF (env-> exceptioncheck () == jni_true) { ENV-> EXCEPTIONDESCRIBE (); ENV-> EXCEPTIONCLEAR (); Printf ("Set the stream loss of the System class."); Return False; } } Loading method of Linux platform The load method of the virtual machine under the Linux platform is basically consistent with the Windows platform. Different places are only different from the method of loading the dynamic library. In the JDK of the Linux platform, the path where the JVM dynamic library is located $ Java_home / jre / lib / i386 / client / libjvm.so $ JAVA_HOME / JRE / LIB / I386 / Server / libjvm.so The sample code to load the dynamic library under Linux is as follows: #include #include #include Typedef void * (* jnicreateproc) (javavm **, jnienv **, void *); Bool Startjvm () { JNIENV * ENV; JavaVM * JVM; JCLASS CLS; JMETHODID MID; JOBJECTARRAY ARGS; // JVM dynamic library path Const char szjvmpath [] = "/usr/jdk1.5.0_01/jre/lib/i386/server/libjvm.so"; // Start the class Const char szstartclass [] = "TEST / JTEST"; // Startup method Const char sstartmethod [] = "main"; // java startup parameters INT NOPTIONCOUNT = 2; JavaVmoption options [2]; Options [1] .Optionstring = "-xmx256m"; Options [0] .Optionstring = "-djava.class.path =. / test.jar ;./ Test1.jar"; JavaVminitargs vm_args; VM_ARGS.VERSION = JNI_Version_1_4; VM_ARGS.OPTION = Options; VM_ARGS.NOPTIONS = NOPTIONCOUNT; VM_ARGS.IGNOREUNRECognized = JNI_TRUE; // Command line parameters INT nParamcount = 2; Const char * szparams [2] = {"arg1", "arg2"}; // Load dynamic library Void * lib_handle = 0; LIB_HANDLE = DLOPEN (SZJVMPATH, RTLD_NOW); IF (! lib_handle) { FPRINTF (stderr, "dlopen failed / n"); Return False; } JNICREATEPROC LIB_FUNC = 0; / / Find JNI_CREATEJAVAVM Process LIB_FUNC = (JNICREATEPROC) DLSYM (lib_handle, "jni_createjavavm"); LIB_FUNC (& JVM, & ENV, & VM_ARGS); // The following is exactly the same as the Windows platform processing process CLS = Env-> FindClass (SzstartClass); IF (CLS == 0) { FPrintf (stderr, "can't find class:% s / n", szstartclass); IF (env-> exceptionoCcurred ()) { ENV-> EXCEPTIONDESCRIBE (); } Return False; } MID = Env-> GetStaticMethodid (CLS, SzstartMethod, "(Ljava / Lang / String;) V"); IF (MID == 0) { FPRINTF (stderr, "can't Find Method:% S / N", SZStartMethod; IF (env-> exceptionoCcurred ()) { ENV-> EXCEPTIONDESCRIBE (); } Return False; } JCLASS STRINGCLASS = ENV-> FindClass ("java / lang / string"); IF (env-> exceptioncheck () == jni_true || StringClass == NULL) { FPrintf (stderr, "find string class error"); IF (env-> exceptionoCcurred ()) { ENV-> EXCEPTIONDESCRIBE (); } Return False; } Args = env-> NewObjectArray (NParamcount, StringClass, NULL); IF (args == 0) { FPRINTF (stderr, "out of memory / n"); IF (env-> exceptionoCcurred ()) { ENV-> EXCEPTIONDESCRIBE (); } Return False; } JString JSTR; For (int i = 0; i { Jstr = env-> newstringutf (Szparams [i]); IF (jstr == 0) { FPRINTF (stderr, "out of memory / n"); IF (env-> exceptionoCcurred ()) { ENV-> EXCEPTIONDESCRIBE (); } JVM-> DestroyjavaVM (); Return False; } ENV-> SetObjectArrayElement (Args, I, JSTR); IF (env-> exceptioncheck () == jni_true) { FPRINTF (stderr, "set param error / n"); IF (env-> exceptionoCcurred ()) { ENV-> EXCEPTIONDESCRIBE (); } JVM-> DestroyjavaVM (); Return -1; } } ENV-> CallStaticVoidMethod (CLS, MID, ARGS); IF (env-> exceptionoCcurred ()) { ENV-> EXCEPTIONDESCRIBE (); Return False; } Return True; } Reference 1. Start the specific method of the Java virtual machine with the creation of sub-process, refer to Open Source Project Java Service Wrapper, http: //wrapper.tanukisoftware.org 2. About the specific description of the JVM dynamic library JNI method, participate in the Sun website About JNI note http://java.sun.com/j2se/1.4.2/docs/guide/jni/ Copyright Notice This article is the foundation of FITA, and it is not allowed to reprint without himself.