首先要去openJDK上下个源码。我这里下到的是openjdk-7-fcs-src-b147-27_jun_2011。
</pre>Java.exe这个程序的启动main函数在<br style="padding:0px; margin:0px; outline:none; list-style:none; border:0px none" /><strong>Java.c (hotspot\src\share\tools\launcher)</strong><br style="padding:0px; margin:0px; outline:none; list-style:none; border:0px none" />main函数的实现如下:<p></p><p style="padding-top:1em; padding-bottom:1em; margin-top:0px; margin-bottom:0px; outline:none; list-style:none; border:0px none; color:rgb(51,51,51); font-family:'Droid Sans',Arial,Verdana,sans-serif; font-size:16px; line-height:24px; orphans:2; widows:2"></p><pre name="code" class="java"> char *jarfile = 0; char *classname = 0; char *s = 0; char *main_class = NULL; int ret; InvocationFunctions ifn; jlong start, end; char jrepath[MAXPATHLEN], jvmpath[MAXPATHLEN]; char ** original_argv = argv; if (getenv("_JAVA_LAUNCHER_DEBUG") != 0) { _launcher_debug = JNI_TRUE; printf("----_JAVA_LAUNCHER_DEBUG----\n"); } #ifndef GAMMA /* * 确定一下版本 是1.4 ,1.5 ,1.6 * Make sure the specified version of the JRE is running. * * There are three things to note about the SelectVersion() routine: * 1) If the version running isn't correct, this routine doesn't * return (either the correct version has been exec'd or an error * was issued). * 2) Argc and Argv in this scope are *not* altered by this routine. * It is the responsibility of subsequent code to ignore the * arguments handled by this routine. * 3) As a side-effect, the variable "main_class" is guaranteed to * be set (if it should ever be set). This isn't exactly the * poster child for structured programming, but it is a small * price to pay for not processing a jar file operand twice. * (Note: This side effect has been disabled. See comment on * bugid 5030265 below.) */ SelectVersion(argc, argv, &main_class); #endif /* ifndef GAMMA */ /* 把参数都拿出来 */ { int i; original_argv = (char**)JLI_MemAlloc(sizeof(char*)*(argc+1)); for(i = 0; i < argc+1; i++) original_argv[i] = argv[i]; } /** * 建立执行环境,比如看看jre 有没有安装,读一下jre的路径,jvm.cfg */ CreateExecutionEnvironment(&argc, &argv, jrepath, sizeof(jrepath), jvmpath, sizeof(jvmpath), original_argv); printf("Using java runtime at: %s\n", jrepath); ifn.CreateJavaVM = 0; ifn.GetDefaultJavaVMInitArgs = 0; //一个计时器之类的 if (_launcher_debug) start = CounterGet(); //载入JVM了 载入dll啦,这里会调用JNI_CreateJavaVM,把JVM线程跑起来 if (!LoadJavaVM(jvmpath, &ifn)) { exit(6); } //计时器,开启JVM线程用了多少时间呢 if (_launcher_debug) { end = CounterGet(); printf("%ld micro seconds to LoadJavaVM\n", (long)(jint)Counter2Micros(end-start)); } #ifdef JAVA_ARGS /* javac, jar and friends. */ progname = "java"; #else /* java, oldjava, javaw and friends */ #ifdef PROGNAME progname = PROGNAME; #else progname = *argv; if ((s = strrchr(progname, FILE_SEPARATOR)) != 0) { progname = s + 1; } #endif /* PROGNAME */ #endif /* JAVA_ARGS */ ++argv; --argc; #ifdef JAVA_ARGS /* 处理命令行的参数了 */ TranslateApplicationArgs(&argc, &argv); if (!AddApplicationOptions()) { exit(1); } #endif /* 设置默认的classpath */ if ((s = getenv("CLASSPATH")) == 0) { s = "."; } #ifndef JAVA_ARGS SetClassPath(s); #endif /* * 解析命令行参数,比如 -jar -cp -help -h -version * -verbose:gc -Xdebug -Xverify -Xrunhprof 等都在这里处理的 */ if (!ParseArguments(&argc, &argv, &jarfile, &classname, &ret, jvmpath)) { exit(ret); } /* 特别处理一下 -jar的classpath */ if (jarfile != 0) { SetClassPath(jarfile); } /* set the -Dsun.java.command pseudo property */ SetJavaCommandLineProp(classname, jarfile, argc, argv); /* Set the -Dsun.java.launcher pseudo property */ SetJavaLauncherProp(); /* set the -Dsun.java.launcher.* platform properties */ SetJavaLauncherPlatformProps(); #ifndef GAMMA /* Show the splash screen if needed */ ShowSplashScreen(); #endif /* * Done with all command line processing and potential re-execs so * clean up the environment. */ (void)UnsetEnv(ENV_ENTRY); #ifndef GAMMA (void)UnsetEnv(SPLASH_FILE_ENV_ENTRY); (void)UnsetEnv(SPLASH_JAR_ENV_ENTRY); JLI_MemFree(splash_jar_entry); JLI_MemFree(splash_file_entry); #endif /* * 线程的栈大小设置,没有设置就是默认的0 */ if (threadStackSize == 0) { struct JDK1_1InitArgs args1_1; memset((void*)&args1_1, 0, sizeof(args1_1)); args1_1.version = JNI_VERSION_1_1; ifn.GetDefaultJavaVMInitArgs(&args1_1); /* ignore return value */ if (args1_1.javaStackSize > 0) { threadStackSize = args1_1.javaStackSize; } } { /* Java的参数 把这个参数给JavaMain方法,执行JavaMain方法 */ struct JavaMainArgs args; args.argc = argc; args.argv = argv; args.jarfile = jarfile; args.classname = classname; args.ifn = ifn; //执行JavaMain,就是Java的主函数 return ContinueInNewThread(JavaMain, threadStackSize, (void*)&args); }
ContinueInNewThread方法创建了一个线程来执行JavaMain,因此后面执行的就是JavaMain。
JavaMain是这样的:
//首先是取得命令行参数 执行的classname jar之类的东西 struct JavaMainArgs *args = (struct JavaMainArgs *)_args; int argc = args->argc; char **argv = args->argv; char *jarfile = args->jarfile; char *classname = args->classname; InvocationFunctions ifn = args->ifn; JavaVM *vm = 0; JNIEnv *env = 0; jstring mainClassName; jclass mainClass; jmethodID mainID; jobjectArray mainArgs; int ret = 0; jlong start, end; /* * Error message to print or display; by default the message will * only be displayed in a window. */ char * message = "Fatal exception occurred. Program will exit."; jboolean messageDest = JNI_FALSE; /* Initialize the virtual machine */ //计时器又来了 if (_launcher_debug) start = CounterGet(); //初始化JVM虚拟机 这里主要去调用了 JNI_CreateJavaVM -->Threads::create_vm //这里做了很多事情,Threads::create_vm 以后再分析。这里只要知道把JVM起来了。 if (!InitializeJVM(&vm, &env, &ifn)) { ReportErrorMessage("Could not create the Java virtual machine.", JNI_TRUE); exit(1); } if (printVersion || showVersion) { PrintJavaVersion(env); if ((*env)->ExceptionOccurred(env)) { ReportExceptionDescription(env); goto leave; } if (printVersion) { ret = 0; message = NULL; goto leave; } if (showVersion) { fprintf(stderr, "\n"); } } /* mainclass和jar总要指定一个吧,不然java没法跑 */ if (jarfile == 0 && classname == 0) { PrintUsage(); message = NULL; goto leave; } #ifndef GAMMA FreeKnownVMs(); /* after last possible PrintUsage() */ #endif //由来一个计时器 if (_launcher_debug) { end = CounterGet(); printf("%ld micro seconds to InitializeJVM\n", (long)(jint)Counter2Micros(end-start)); } /* 拿到了应用的参数 打印一下看看 */ if (_launcher_debug) { int i = 0; printf("Main-Class is '%s'\n", classname ? classname : ""); printf("Apps' argc is %d\n", argc); for (; i < argc; i++) { printf(" argv[%2d] = '%s'\n", i, argv[i]); } } ret = 1; /* * 取得MainClass */ if (jarfile != 0) { mainClassName = GetMainClassName(env, jarfile); if ((*env)->ExceptionOccurred(env)) { ReportExceptionDescription(env); goto leave; } if (mainClassName == NULL) { const char * format = "Failed to load Main-Class manifest " "attribute from\n%s"; message = (char*)JLI_MemAlloc((strlen(format) + strlen(jarfile)) * sizeof(char)); sprintf(message, format, jarfile); messageDest = JNI_TRUE; goto leave; } classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); if (classname == NULL) { ReportExceptionDescription(env); goto leave; } //载入类转为mainClass,它是jclass结构体 mainClass = LoadClass(env, classname); if(mainClass == NULL) { /* exception occured */ const char * format = "Could not find the main class: %s. Program will exit."; ReportExceptionDescription(env); message = (char *)JLI_MemAlloc((strlen(format) + strlen(classname)) * sizeof(char) ); messageDest = JNI_TRUE; sprintf(message, format, classname); goto leave; } (*env)->ReleaseStringUTFChars(env, mainClassName, classname); } else { //前面处理jar的情况 这里处理没有指定jar的情况 mainClassName = NewPlatformString(env, classname); if (mainClassName == NULL) { const char * format = "Failed to load Main Class: %s"; message = (char *)JLI_MemAlloc((strlen(format) + strlen(classname)) * sizeof(char) ); sprintf(message, format, classname); messageDest = JNI_TRUE; goto leave; } classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); if (classname == NULL) { ReportExceptionDescription(env); goto leave; } //上面处理utf8的,我也没看过,应该不是核心 不管了,这里载入MainClass mainClass = LoadClass(env, classname); if(mainClass == NULL) { /* exception occured */ const char * format = "Could not find the main class: %s. Program will exit."; ReportExceptionDescription(env); message = (char *)JLI_MemAlloc((strlen(format) + strlen(classname)) * sizeof(char) ); messageDest = JNI_TRUE; sprintf(message, format, classname); goto leave; } (*env)->ReleaseStringUTFChars(env, mainClassName, classname); } /* 找到MainClass main方法 当然这个main必须是由String[]参数的 */ mainID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V"); //找不到就挂了呗 if (mainID == NULL) { if ((*env)->ExceptionOccurred(env)) { ReportExceptionDescription(env); } else { message = "No main method found in specified class."; messageDest = JNI_TRUE; } goto leave; } { /* main函数必须是public的 */ jint mods; jmethodID mid; //这里拿出来的是 java.lang.reflect.Method jobject obj = (*env)->ToReflectedMethod(env, mainClass, mainID, JNI_TRUE); if( obj == NULL) { /* exception occurred */ ReportExceptionDescription(env); goto leave; } // 一个反射 调用的 java.lang.reflect.Method.getModifiers // 找到修饰符 看看是不是public mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, obj), "getModifiers", "()I"); if ((*env)->ExceptionOccurred(env)) { ReportExceptionDescription(env); goto leave; } // public就是1,这个看java的代码里就能找到所有的定义 mods = (*env)->CallIntMethod(env, obj, mid); if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */ message = "Main method not public."; messageDest = JNI_TRUE; goto leave; } } /* 把Java程序的参数都放好 */ mainArgs = NewPlatformStringArray(env, argv, argc); if (mainArgs == NULL) { ReportExceptionDescription(env); goto leave; } /* 调用Java的Main方法 */ (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); /* * * 遇到异常返回值就是非0 */ ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1; /* * Detach the main thread so that it appears to have ended when * the application's main method exits. This will invoke the * uncaught exception handler machinery if main threw an * exception. An uncaught exception handler cannot change the * launcher's return code except by calling System.exit. */ if ((*vm)->DetachCurrentThread(vm) != 0) { message = "Could not detach main thread."; messageDest = JNI_TRUE; ret = 1; goto leave; } message = NULL; leave: /* * 这里就是退出了 * Wait for all non-daemon threads to end, then destroy the VM. * This will actually create a trivial new Java waiter thread * named "DestroyJavaVM", but this will be seen as a different * thread from the one that executed main, even though they are * the same C thread. This allows mainThread.join() and * mainThread.isAlive() to work as expected. */ (*vm)->DestroyJavaVM(vm); if(message != NULL && !noExitErrorMessage) ReportErrorMessage(message, messageDest); return ret;
如果您会Java,但是还不会JVM,还看不懂JVM,那么来学习下葛老师开设的深入浅出JVM课程吧。http://www.dataguru.cn/myclassnew.php?mod=new_basicforlesson&op=basic&lessonid=208
或者加入QQ 群397196583,一起来讨论JVM看不通看不懂的地方。
用Java这么久,你能说出Java启动过程嘛?别吹了,来看看
原文地址:http://blog.csdn.net/xinaij/article/details/39029595