JVM启动流程(JDK8)
JVM的启动入口是位于jdk/src/share/bin/java.c的JLI_Launch函数,其定义如下:
int
JLI_Launch(int argc, char ** argv, /* main argc, argc */int jargc, const char** jargv, /* java args */int appclassc, const char** appclassv, /* app classpath */const char* fullversion, /* full version defined */const char* dotversion, /* dot version defined */const char* pname, /* program name */const char* lname, /* launcher name */jboolean javaargs, /* JAVA_ARGS */jboolean cpwildcard, /* classpath wildcard */jboolean javaw, /* windows-only javaw */jint ergo_class /* ergnomics policy */
);
1.初始化
InitLauncher(javaw); //初始化启动器
DumpState(); //打印当前状态
//确保开启启动器跟踪状态
if (JLI_IsTraceLauncher()) {int i;printf("Command line args:\n");for (i = 0; i < argc ; i++) {printf("argv[%d] = %s\n", i, argv[i]);}AddOption("-Dsun.java.launcher.diag=true", NULL);
}
2.选择jre版本
解析参数,读取manifest文件,jre版本校验,加载jre以便确认是否存在,最后将相关环境变量放置好。
SelectVersion(argc, argv, &main_class);
3.创建JVM执行环境
确定数据模型,是32位还是64位,以及jvm本身的一些配置在jvm.cfg文件中读取和解析
CreateExecutionEnvironment(&argc, &argv,jrepath, sizeof(jrepath), //jre路径jvmpath, sizeof(jvmpath), //jvm路径jvmcfg, sizeof(jvmcfg)); //jvm配置文件
4.加载jvm.so库
动态加载jvm.so这个共享库,并把jvm.so中的相关函数导出并且初始化
if (!IsJavaArgs()) {// 设置一些特殊的环境变量SetJvmEnvironment(argc,argv);}ifn.CreateJavaVM = 0;ifn.GetDefaultJavaVMInitArgs = 0;if (JLI_IsTraceLauncher()) {start = CounterGet(); // 记录启动时间}// 加载VM, 重中之重if (!LoadJavaVM(jvmpath, &ifn)) {return(6);}if (JLI_IsTraceLauncher()) {end = CounterGet();}JLI_TraceLauncher("%ld micro seconds to LoadJavaVM\n",(long)(jint)Counter2Micros(end-start));++argv;--argc;// 解析更多参数信息if (IsJavaArgs()) {/* Preprocess wrapper arguments */TranslateApplicationArgs(jargc, jargv, &argc, &argv);if (!AddApplicationOptions(appclassc, appclassv)) {return(1);}} else {/* Set default CLASSPATH */cpath = getenv("CLASSPATH");if (cpath == NULL) {cpath = ".";}SetClassPath(cpath);}/* Parse command line options; if the return value of* ParseArguments is false, the program should exit.*/// 解析参数if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath)){return(ret);}/* Override class path if -jar flag was specified */if (mode == LM_JAR) {SetClassPath(what); /* Override class path */}/* set the -Dsun.java.command pseudo property */SetJavaCommandLineProp(what, argc, argv);/* Set the -Dsun.java.launcher pseudo property */SetJavaLauncherProp();/* set the -Dsun.java.launcher.* platform properties */SetJavaLauncherPlatformProps();
5.初始化jvm
return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
JVMInit函数最后一句是
return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
继续看ContinueInNewThread函数,会进入ContinueInNewThread0函数
ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);
实现在新的线程中执行JavaMain函数
- 初始化虚拟机,如果报错直接退出。
/* Initialize the virtual machine */
start = CounterGet();
if (!InitializeJVM(&vm, &env, &ifn)) {JLI_ReportErrorMessage(JVM_ERROR1);exit(1);
}
- 加载主类
mainClass = LoadMainClass(env, mode, what);
- 获取Application Main Class
某些没有主方法的Java程序比如JavaFX应用,会获取Application Main Class
/** In some cases when launching an application that needs a helper, e.g., a* JavaFX application with no main method, the mainClass will not be the* applications own main class but rather a helper class. To keep things* consistent in the UI we need to track and report the application main class.*/
appClass = GetApplicationClass(env);
- 初始化完成
PostJVMInit(env, appClass, vm);
- 获取主类中的主方法
mainID = (*env)->GetStaticMethodID(env, mainClass, "main","([Ljava/lang/String;)V");
在字节码中void main(String[] args)表示为([Ljava/lang/String;)V
- 调用主方法
/* Invoke main method. */
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
- LEAVE函数结束线程,销毁JVM
/** The launcher's exit code (in the absence of calls to* System.exit) will be non-zero if main threw an exception.*/
ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
LEAVE();
流程图如下: