从开始接触java写第一个hello world
程序时,我们就知道,编译完成后只要在命令行里输入java yourClassFilename这个java命令,就能启动jvm虚拟机运行你的java程序。但是操作系统到底怎么启动jvm虚拟机的,那就要分析分析jdk的源码了。
启动步骤
从openjdk官网下载源码进行分析,jvm启动过程分为两部分:launch启动器和hotspot vm自身启动。大概步骤如下。
- 准备工作,包括查找jre、jvm
- 装载动态链接库
- 解析jvm参数
- 启动虚拟机
- 加载主类,并调用主类的main()方法
源码分析
c++程序的入口点,自然是唯一的main函数,main函数在openjdk/jdk/src/share/bin/main.c文件中,其调用了JLI_Launch启动,代码在openjdk/jdk/src/share/bin/java.c中。
main(int argc, char **argv)
{
int margc;
char** margv;
const jboolean const_javaw = JNI_FALSE; //是否以javaw启动
margc = argc;
margv = argv;
return JLI_Launch(margc, margv,
sizeof(const_jargs) / sizeof(char *), const_jargs,
sizeof(const_appclasspath) / sizeof(char *), const_appclasspath,
FULL_VERSION,
DOT_VERSION,
(const_progname != NULL) ? const_progname : *margv,
(const_launcher != NULL) ? const_launcher : *margv,
(const_jargs != NULL) ? JNI_TRUE : JNI_FALSE,
const_cpwildcard, const_javaw, const_ergo_class);
}
JLI_Launch方法代码如下,整个jvm启动逻辑都在这里了。首先SelectVersion()方法确定系统安装了你指定的JRE版本,接着CreateExecutionEnvironment
/*
* Entry point.
*/
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 /* ergonomics class policy */
)
{
//启动模式,如果非jar,非main class启动,那么程序将退出
int mode = LM_UNKNOWN;
//存储启动类名
char *what = NULL;
//储存classpath
char *cpath = 0;
//主类引用
char *main_class = NULL;
int ret;
//加载JAVA_DLL时,将一些函数链接到此变量中
InvocationFunctions ifn;
jlong start, end;
//jvm路径
char jvmpath[MAXPATHLEN];
//jre路径
char jrepath[MAXPATHLEN];
//jvm.cfg路径
char jvmcfg[MAXPATHLEN];
_fVersion = fullversion;
_dVersion = dotversion;
_launcher_name = lname;
_program_name = pname;
_is_java_args = javaargs;
_wc_enabled = cpwildcard;
_ergo_policy = ergo;
//根据环境变量中的“JLDEBUG_ENV_ENTRY”参数来设置_launcher_debug,表示是否打印调试信息
/*
* Initialize platform specific settings
*/
InitLauncher(javaw);
//printf一些调试的提示,无其他作用
DumpState();
//判断_launcher_debug输出调试信息
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);
}
/*
* 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.);ll;;
*/
// jre相关,从命令行-version:或从Mainfest获取jre版本,并使用LocateJRE()从注册表键值JavaHome定位到jre路径,最后使用ExecJRE()小启动一把jre
SelectVersion(argc, argv, &main_class);
//准备环境,先根据java.exe目录查找jdk的jre路径,称为private jre,取不到则取注册表的jre路径,称为public jre。然后查找到jre/bin/java.dll为java虚拟机。查找jre/lib/amd64/jvm.cfg文件,获取虚拟机启动类型。根据指定虚拟机类型如server,查找jre/bin/server/jvm.dll虚拟机文件。
CreateExecutionEnvironment(&argc, &argv,
jrepath, sizeof(jrepath),
jvmpath, sizeof(jvmpath),
jvmcfg, sizeof(jvmcfg));
ifn.CreateJavaVM = 0;
ifn.GetDefaultJavaVMInitArgs = 0;
if (JLI_IsTraceLauncher()) {
start = CounterGet();
}
//加载jvm.dll到内存,并将动态库中的函数链接至ifn.CreateJavaVM,ifn.GetDefaultJavaVMInitArgs
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 = ".";
}
//设置CLASSPATH,并设置虚拟机参数Xms, Xmx, Xss
SetClassPath(cpath);
}
/* Parse command line options; if the return value of
* ParseArguments is false, the program should exit.
*/
//解析所有option参数,并将其存到JavaVMOption *options变量中
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();
//展示splash screen,开启新线程去初始化JVM,最终调用jvm.dll中的CreateJavaVM启动虚拟机。并通过LoadMainClass()方法获取main class,找到bootstrap类加载器加载sun/launcher/LauncherHelper,执行该类的checkAndLoadMain方法,初始化ExtClassLoader和AppClassLoader类加载器,执行sun.misc.Launcher.getLauncher()的getClassLoader()获取AppClassLoader加载主类,调用main class的main方法
return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
}
总结的不是很好,有空我再多写几篇。
网友评论