这一节的主要内容是讲如何在native程序中嵌入一个java VM。一个java的实现通常是作为一个native库运行。native程序可以和这个库链接,并且用调用接口来加载java VM。事实上,JDK或者java2 sdk的标准启动命令,就是一个简单链接了java VM的c程序。该启动器,解析命令行参数、加载VM、并且通过调用接口运行java程序。
创建java虚拟机
先看一C程序,它加载一个java VM并且调用Prog.main方法:下面是C的代码:public class Prog { public static void main(String[] args) { System.out.println("Hello World " + args[0]); } }
#include <jni.h> #define PATH_SEPARATOR ';' /* define it to be ':' on Solaris */ #define USER_CLASSPATH "." /* where Prog.class is */ main() { JNIEnv *env; JavaVM *jvm; jint res; jclass cls; jmethodID mid; jstring jstr; jclass stringClass; jobjectArray args; #ifdef JNI_VERSION_1_2 JavaVMInitArgs vm_args; JavaVMOption options[1]; options[0].optionString = "-Djava.class.path=" USER_CLASSPATH; vm_args.version = 0x00010002; vm_args.options = options; vm_args.nOptions = 1; vm_args.ignoreUnrecognized = JNI_TRUE; /* Create the Java VM */ res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); #else JDK1_1InitArgs vm_args; char classpath[1024]; vm_args.version = 0x00010001; JNI_GetDefaultJavaVMInitArgs(&vm_args); /* Append USER_CLASSPATH to the default system class path */ sprintf(classpath, "%s%c%s", vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH); vm_args.classpath = classpath; /* Create the Java VM */ res = JNI_CreateJavaVM(&jvm, &env, &vm_args); #endif /* JNI_VERSION_1_2 */ if (res < 0) { fprintf(stderr, "Can't create Java VM\n"); exit(1); } cls = (*env)->FindClass(env, "Prog"); if (cls == NULL) { goto destroy; } mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V"); if (mid == NULL) { goto destroy; } jstr = (*env)->NewStringUTF(env, " from C!"); if (jstr == NULL) { goto destroy; } stringClass = (*env)->FindClass(env, "java/lang/String"); args = (*env)->NewObjectArray(env, 1, stringClass, jstr); if (args == NULL) { goto destroy; } (*env)->CallStaticVoidMethod(env, cls, mid, args); destroy: if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); } (*jvm)->DestroyJavaVM(jvm); }
和java VM一起链接native程序
如何和java VM链接,依赖于native程序对准备知部署于一个特定的java VM还是和各种VM实现一起工作。
和一个已知的java VM链接
例如和上诉代码invoke.c一起链接:1)在linux:cc -I<jni.h dir> -L<libjava.so dir> -lthread -ljava invoke.c
-lthread选项表示需要native线程支持。-java选项指定了libjava.so是linux中java VM实现的动态库。
2)在win32:cl -I<jni.h dir> -MD invoke.c -link <javai.lib dir>\javai.lib
-MD选项保证了native程序时和win32多线程C库一起链接的。
和未知java VM 链接
可以以动态的方式和未知的java VM链接在一起。这样一套程序,就可以在不同的vm实现上执行了。这个方案的核心是,在运行时加载动态链接库。(和前面提到的链接一个已知的java vm,这两种方式,其实就是如何使用动态库的两种方式)。
例如为一个创建java VM的 函数寻找一个入口点:/* Win32 version */ void *JNU_FindCreateJavaVM(char *vmlibpath) { /* load the dynamical libraries */ HINSTANCE hVM = LoadLibrary(vmlibpath); if (hVM == NULL) { return NULL; } /* get the address of function "JNI_CreateJavaVM" */ return GetProcAddress(hVM, "JNI_CreateJavaVM"); } /* linux version */ void *JNU_FindCreateJavaVM(char *vmlibpath) { /* load the dynamical libraries */ void *libVM = dlopen(vmlibpath, RTLD_LAZY); if (libVM == NULL) { return NULL; } /* get the address of function "JNI_CreateJavaVM" */ return dlsym(libVM, "JNI_CreateJavaVM"); }
关联native 线程
假设你有一个用native C实现的多线程web server程序。当HTTP请求进来时, 服务端,创建多个native线程来并发地处理请求。我们可能希望在服务中嵌入一个java虚拟机,这样,同一时刻可以有多个线程在这个虚拟机上进行操作。例如:
![]()
服务器产生的native方法的生命周期,可能比java VM要短,于是我们要有一种能够将线程附加到已经存在的java VM上的方法,使得VM能够执行附加了的native线程的JNI调用。并且在完成后分离该线程,而不中断其他附加的线程。
下面例子展示了,如何将一个native线程通过调用接口来附加到java VM上:/* Note: This program only works on Win32 */ #include <windows.h> #include <jni.h> JavaVM *jvm; /* The virtual machine instance */ #define PATH_SEPARATOR ';' #define USER_CLASSPATH "." /* where Prog.class is */ void thread_fun(void *arg) { jint res; jclass cls; jmethodID mid; jstring jstr; jclass stringClass; jobjectArray args; JNIEnv *env; char buf[100]; int threadNum = (int)arg; /* Pass NULL as the third argument */ #ifdef JNI_VERSION_1_2 res = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); #else res = (*jvm)->AttachCurrentThread(jvm, &env, NULL); #endif if (res < 0) { fprintf(stderr, "Attach failed\n"); return; } cls = (*env)->FindClass(env, "Prog"); if (cls == NULL) { goto detach; } mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V"); if (mid == NULL) { goto detach; } sprintf(buf, " from Thread %d", threadNum); jstr = (*env)->NewStringUTF(env, buf); if (jstr == NULL) { goto detach; } stringClass = (*env)->FindClass(env, "java/lang/String"); args = (*env)->NewObjectArray(env, 1, stringClass, jstr); if (args == NULL) { goto detach; } (*env)->CallStaticVoidMethod(env, cls, mid, args); detach: if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); } (*jvm)->DetachCurrentThread(jvm); } main() { JNIEnv *env; int i; jint res; #ifdef JNI_VERSION_1_2 JavaVMInitArgs vm_args; JavaVMOption options[1]; options[0].optionString = "-Djava.class.path=" USER_CLASSPATH; vm_args.version = 0x00010002; vm_args.options = options; vm_args.nOptions = 1; vm_args.ignoreUnrecognized = TRUE; /* Create the Java VM */ res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); #else JDK1_1InitArgs vm_args; char classpath[1024]; vm_args.version = 0x00010001; JNI_GetDefaultJavaVMInitArgs(&vm_args); /* Append USER_CLASSPATH to the default system class path */ sprintf(classpath, "%s%c%s",vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH); vm_args.classpath = classpath; /* Create the Java VM */ res = JNI_CreateJavaVM(&jvm, &env, &vm_args); #endif /* JNI_VERSION_1_2 */ if (res < 0) { fprintf(stderr, "Can't create Java VM\n"); exit(1); } for (i = 0; i < 5; i++) /* We pass the thread number to every thread */ _beginthread(thread_fun, 0, (void *)i); Sleep(1000); /* wait for threads to start */ (*jvm)->DestroyJavaVM(jvm); }
执行结果:Hello World from thread 1 Hello World from thread 0 Hello World from thread 4 Hello World from thread 2 Hello World from thread 3
06-13
08-19
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交