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 */
);
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);
}
解析参数,读取manifest文件,jre版本校验,加载jre以便确认是否存在,最后将相关环境变量放置好。
SelectVersion(argc, argv, &main_class);
确定数据模型,是32位还是64位,以及jvm本身的一些配置在jvm.cfg文件中读取和解析
CreateExecutionEnvironment(&argc, &argv,
jrepath, sizeof(jrepath), //jre路径
jvmpath, sizeof(jvmpath), //jvm路径
jvmcfg, sizeof(jvmcfg)); //jvm配置文件
动态加载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();
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);
某些没有主方法的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);
/*
* 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();
流程图如下: