首先要去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 ></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;