JVM启动流程(JDK8)

JVM的启动入口是位于jdk/src/share/bin/java.c的JLI_Launch函数,其定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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.初始化

1
2
3
4
5
6
7
8
9
10
11
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以便确认是否存在,最后将相关环境变量放置好。

1
SelectVersion(argc, argv, &main_class);

3.创建JVM执行环境

确定数据模型,是32位还是64位,以及jvm本身的一些配置在jvm.cfg文件中读取和解析

1
2
3
4
CreateExecutionEnvironment(&argc, &argv,
jrepath, sizeof(jrepath), //jre路径
jvmpath, sizeof(jvmpath), //jvm路径
jvmcfg, sizeof(jvmcfg)); //jvm配置文件

4.加载jvm.so库

动态加载jvm.so这个共享库,并把jvm.so中的相关函数导出并且初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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

1
return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);

JVMInit函数最后一句是

1
return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);

继续看ContinueInNewThread函数,会进入ContinueInNewThread0函数

1
ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);

实现在新的线程中执行JavaMain函数

  1. 初始化虚拟机,如果报错直接退出。
1
2
3
4
5
6
/* Initialize the virtual machine */
start = CounterGet();
if (!InitializeJVM(&vm, &env, &ifn)) {
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
  1. 加载主类
1
mainClass = LoadMainClass(env, mode, what);
  1. 获取Application Main Class

某些没有主方法的Java程序比如JavaFX应用,会获取Application Main Class

1
2
3
4
5
6
7
/*
* 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);
  1. 初始化完成
1
PostJVMInit(env, appClass, vm);
  1. 获取主类中的主方法
1
mainID = (*env)->GetStaticMethodID(env, mainClass, "main","([Ljava/lang/String;)V");

在字节码中void main(String[] args)表示为([Ljava/lang/String;)V

  1. 调用主方法
1
2
/* Invoke main method. */
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
  1. LEAVE函数结束线程,销毁JVM
1
2
3
4
5
6
/*
* 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();

流程图如下:
avatar