概述
最近在看Java并发编程实践和Inside JVM两本书,发现如果不真正的了解底层运作,那么永远是雾里看花。因此从http://openjdk.java.net/groups/hotspot/上下载了源代码,准备研究一番。要想完全研究懂我觉得得对计算机体系结构,C,C++编程,Linux内核都有比较深入的理解。由于并非从事JVM开发工作,因此不会研究的那么深入。入手就从“java 类名”这个Hello World的命令开始吧,简要的看一下JVM是如何运行起来执行main函数的。
这个实现使用的是C代码(位于share/tools/launcher中的java.c),关注一下是如何执行这个命令的。
过程主要就是按照参数加载虚拟机,然后找到指定类,执行其main方法。
整个C程序是从main函数开始:
1
int
2 main( int argc, char ** argv)
3 {
4 char *jarfile = 0;
5 char *classname = 0;
6 char *s = 0;
7 char *main_class = NULL;
8 int ret;
设置运行时环境:
2 main( int argc, char ** argv)
3 {
4 char *jarfile = 0;
5 char *classname = 0;
6 char *s = 0;
7 char *main_class = NULL;
8 int ret;
CreateExecutionEnvironment(&argc, &argv,
jrepath, sizeof(jrepath),
jvmpath, sizeof(jvmpath),
original_argv);
加载JVM:
jrepath, sizeof(jrepath),
jvmpath, sizeof(jvmpath),
original_argv);
1
if (!LoadJavaVM(jvmpath, &ifn)) {
2 exit(6);
3 }
设置Classpath:
2 exit(6);
3 }
SetClassPath
设置各种参数
1
/*
set the -Dsun.java.command pseudo property
*/
2 SetJavaCommandLineProp(classname, jarfile, argc, argv);
3
4 /* Set the -Dsun.java.launcher pseudo property */
5 SetJavaLauncherProp();
6
7 /* set the -Dsun.java.launcher.* platform properties */
8 SetJavaLauncherPlatformProps();
2 SetJavaCommandLineProp(classname, jarfile, argc, argv);
3
4 /* Set the -Dsun.java.launcher pseudo property */
5 SetJavaLauncherProp();
6
7 /* set the -Dsun.java.launcher.* platform properties */
8 SetJavaLauncherPlatformProps();
设置线程栈大小:
1
if (threadStackSize == 0) {
2 struct JDK1_1InitArgs args1_1;
3 memset(( void*)&args1_1, 0, sizeof(args1_1));
4 args1_1.version = JNI_VERSION_1_1;
5 ifn.GetDefaultJavaVMInitArgs(&args1_1); /* ignore return value */
6 if (args1_1.javaStackSize > 0) {
7 threadStackSize = args1_1.javaStackSize;
8 }
9 }
2 struct JDK1_1InitArgs args1_1;
3 memset(( void*)&args1_1, 0, sizeof(args1_1));
4 args1_1.version = JNI_VERSION_1_1;
5 ifn.GetDefaultJavaVMInitArgs(&args1_1); /* ignore return value */
6 if (args1_1.javaStackSize > 0) {
7 threadStackSize = args1_1.javaStackSize;
8 }
9 }
创建一个新的线程创建JVM并调用main方法。
1 {
/*
Create a new thread to create JVM and invoke main method
*/
2 struct JavaMainArgs args;
3
4 args.argc = argc;
5 args.argv = argv;
6 args.jarfile = jarfile;
7 args.classname = classname;
8 args.ifn = ifn;
9
10 return ContinueInNewThread(JavaMain, threadStackSize, ( void*)&args);
11 }
最后在这个线程里调用了JavaMain,接下来是JavaMain的代码:
2 struct JavaMainArgs args;
3
4 args.argc = argc;
5 args.argv = argv;
6 args.jarfile = jarfile;
7 args.classname = classname;
8 args.ifn = ifn;
9
10 return ContinueInNewThread(JavaMain, threadStackSize, ( void*)&args);
11 }
1
int JNICALL
2 JavaMain( void * _args)
3 {
4 struct JavaMainArgs *args = ( struct JavaMainArgs *)_args;
5 int argc = args->argc;
6 char **argv = args->argv;
7 char *jarfile = args->jarfile;
8 char *classname = args->classname;
下面就是调用Java里的main方法了,当然首先是加载类:
2 JavaMain( void * _args)
3 {
4 struct JavaMainArgs *args = ( struct JavaMainArgs *)_args;
5 int argc = args->argc;
6 char **argv = args->argv;
7 char *jarfile = args->jarfile;
8 char *classname = args->classname;
1
if (jarfile != 0) {
2 mainClassName = GetMainClassName(env, jarfile);
根据class的名字加载class:
2 mainClassName = GetMainClassName(env, jarfile);
1 classname = (
char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
2 if (classname == NULL) {
3 ReportExceptionDescription(env);
4 goto leave;
5 }
6 mainClass = LoadClass(env, classname);
还有其他情况:
2 if (classname == NULL) {
3 ReportExceptionDescription(env);
4 goto leave;
5 }
6 mainClass = LoadClass(env, classname);
1 mainClassName = NewPlatformString(env, classname);
2 if (mainClassName == NULL) {
3 const char * format = "Failed to load Main Class: %s";
4 message = ( char *)JLI_MemAlloc((strlen(format) + strlen(classname)) *
5 sizeof( char) );
6 sprintf(message, format, classname);
7 messageDest = JNI_TRUE;
8 goto leave;
9 }
10 classname = ( char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
11 if (classname == NULL) {
12 ReportExceptionDescription(env);
13 goto leave;
14 }
15 mainClass = LoadClass(env, classname);
接下来就是调用java中的main方法了,这是一个静态方法:
2 if (mainClassName == NULL) {
3 const char * format = "Failed to load Main Class: %s";
4 message = ( char *)JLI_MemAlloc((strlen(format) + strlen(classname)) *
5 sizeof( char) );
6 sprintf(message, format, classname);
7 messageDest = JNI_TRUE;
8 goto leave;
9 }
10 classname = ( char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
11 if (classname == NULL) {
12 ReportExceptionDescription(env);
13 goto leave;
14 }
15 mainClass = LoadClass(env, classname);
1
/*
Get the application's main method
*/
2 mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
3 "([Ljava/lang/String;)V");
2 mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
3 "([Ljava/lang/String;)V");
确认main方法是public的
1 {
/*
Make sure the main method is public
*/
2 jint mods;
3 jmethodID mid;
4 jobject obj = (*env)->ToReflectedMethod(env, mainClass,
5 mainID, JNI_TRUE);
6
7 if( obj == NULL) { /* exception occurred */
8 ReportExceptionDescription(env);
9 goto leave;
10 }
11
12 mid =
13 (*env)->GetMethodID(env,
14 (*env)->GetObjectClass(env, obj),
15 "getModifiers", "()I");
2 jint mods;
3 jmethodID mid;
4 jobject obj = (*env)->ToReflectedMethod(env, mainClass,
5 mainID, JNI_TRUE);
6
7 if( obj == NULL) { /* exception occurred */
8 ReportExceptionDescription(env);
9 goto leave;
10 }
11
12 mid =
13 (*env)->GetMethodID(env,
14 (*env)->GetObjectClass(env, obj),
15 "getModifiers", "()I");
1 mods = (*env)->CallIntMethod(env, obj, mid);
2 if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) */
3 message = "Main method not public.";
4 messageDest = JNI_TRUE;
5 goto leave;
6 }
2 if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) */
3 message = "Main method not public.";
4 messageDest = JNI_TRUE;
5 goto leave;
6 }
构建参数:
1
/*
Build argument array
*/
2 mainArgs = NewPlatformStringArray(env, argv, argc);
3 if (mainArgs == NULL) {
4 ReportExceptionDescription(env);
5 goto leave;
6 }
调用main方法:
2 mainArgs = NewPlatformStringArray(env, argv, argc);
3 if (mainArgs == NULL) {
4 ReportExceptionDescription(env);
5 goto leave;
6 }
1
/*
Invoke main method.
*/
2 (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
脱离线程:
2 (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
1
/*
2 * Detach the main thread so that it appears to have ended when
3 * the application's main method exits. This will invoke the
4 * uncaught exception handler machinery if main threw an
5 * exception. An uncaught exception handler cannot change the
6 * launcher's return code except by calling System.exit.
7 */
8 if ((*vm)->DetachCurrentThread(vm) != 0) {
9 message = "Could not detach main thread.";
10 messageDest = JNI_TRUE;
11 ret = 1;
12 goto leave;
13 }
2 * Detach the main thread so that it appears to have ended when
3 * the application's main method exits. This will invoke the
4 * uncaught exception handler machinery if main threw an
5 * exception. An uncaught exception handler cannot change the
6 * launcher's return code except by calling System.exit.
7 */
8 if ((*vm)->DetachCurrentThread(vm) != 0) {
9 message = "Could not detach main thread.";
10 messageDest = JNI_TRUE;
11 ret = 1;
12 goto leave;
13 }
销毁虚拟机,得等所有线程都结束,因为虚拟机是一个守护线程:
1 leave:
2 /*
3 * Wait for all non-daemon threads to end, then destroy the VM.
4 * This will actually create a trivial new Java waiter thread
5 * named "DestroyJavaVM", but this will be seen as a different
6 * thread from the one that executed main, even though they are
7 * the same C thread. This allows mainThread.join() and
8 * mainThread.isAlive() to work as expected.
9 */
10 (*vm)->DestroyJavaVM(vm);
2 /*
3 * Wait for all non-daemon threads to end, then destroy the VM.
4 * This will actually create a trivial new Java waiter thread
5 * named "DestroyJavaVM", but this will be seen as a different
6 * thread from the one that executed main, even though they are
7 * the same C thread. This allows mainThread.join() and
8 * mainThread.isAlive() to work as expected.
9 */
10 (*vm)->DestroyJavaVM(vm);
回过头来比较重要的几个方法:
1 LoadClass
2 GetStaticMethodID
3 GetMethodID
4 CallIntMethod
5 CallStaticVoidMethod
6 ContinueInNewThread
7 LoadJavaVM
这些方法基本上就是我们平常写代码比较关注的了。
LoadClass
1
/*
2 * Loads a class, convert the '.' to '/'.
3 */
4 static jclass
5 LoadClass(JNIEnv *env, char *name)
6 {
7 char *buf = JLI_MemAlloc(strlen(name) + 1);
8 char *s = buf, *t = name, c;
9 jclass cls;
10 jlong start, end;
11
12 if (_launcher_debug)
13 start = CounterGet();
14
15 do {
16 c = *t++;
17 *s++ = (c == '.') ? '/' : c;
18 } while (c != '
2 * Loads a class, convert the '.' to '/'.
3 */
4 static jclass
5 LoadClass(JNIEnv *env, char *name)
6 {
7 char *buf = JLI_MemAlloc(strlen(name) + 1);
8 char *s = buf, *t = name, c;
9 jclass cls;
10 jlong start, end;
11
12 if (_launcher_debug)
13 start = CounterGet();
14
15 do {
16 c = *t++;
17 *s++ = (c == '.') ? '/' : c;
18 } while (c != '