基于OpenJdk 标签 jdk8-b109版本分析
HotSpot是由Oracle开发的Java虚拟机(JVM)的一种实现,也是使用最广泛的JVM之一。
完成编译配置后,会在根目录生成Makefile文件,如何编译jdk8 Windows平台编译Jdk8
Makefile 中找到了 Main.gmk,构建入口由此开始。
- ifeq ($(words $(SPEC)),1)
- # We are building a single configuration. This is the normal case. Execute the Main.gmk file.
- include $(root_dir)/make/Main.gmk
- else
根目录/make/Main.gmk,这里可以看到构建目标 :jdk,具体构建过程在 BuildJdk.gmk文件。
- jdk: langtools hotspot corba jaxp jaxws jdk-only
- jdk-only: start-make
- @$(call TargetEnter)
- @($(CD) $(JDK_TOPDIR)/make && $(BUILD_LOG_WRAPPER) $(MAKE) $(MAKE_ARGS) -f BuildJdk.gmk $(JDK_TARGET))
- @$(call TargetExit)
根目录/jdk/make/BuildJdk.gmk,jvm启动器由构建目标 launchers 实现,由 CompileLaunchers.gmk 提供构建过程。
- launchers: libs launchers-only
- launchers-only:
- # Finally compile the launchers.
- +$(MAKE) -f CompileLaunchers.gmk
最终定位到hotspot入口 根目录/jdk/src/share/bin/main.c
- $(call SetupNativeCompilation,BUILD_LAUNCHER_$1, \
- SRC := $(JDK_TOPDIR)/src/share/bin, \
- INCLUDE_FILES := main.c, \
- LANG := C, \
jdk/src/share/bin/main.c
main 并没有太多复杂的操作,它对程序参数解析并生成后续jvm可用的参数集合,然后调用
JLI_Launch 进一步处理。
- 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);
jdk/src/share/bin/java.c
方法定义及参数描述清晰明了这里不再赘述。
- /*
- * 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 */
- )
- //设置跟踪调试标识
- InitLauncher(javaw);
-
- //如果启用了跟踪标识则输出启动参数
- 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);
- }
选择JRE,并解析jvm参数、确认jre可使用。
JLI_Launch->SelectVersion->LocateJRE->ExecJRE
SelectVersion
- //保存jre版本信息
- if (version != NULL)
- info.jre_version = version;
- if (restrict_search != -1)
- info.jre_restrict_search = restrict_search;
-
- //入口类从命令行传入
- if (info.main_class != NULL)
- *main_class = JLI_StringDup(info.main_class);
-
- //没有jre版本信息,后续代码无法定位jre路径和进行版本检查
- if (info.jre_version == NULL) {
- JLI_FreeManifest();
- JLI_MemFree(new_argv);
- return;
- }
-
- //jre版本信息格式是否正确
- if (!JLI_ValidVersionString(info.jre_version)) {
- JLI_ReportErrorMessage(SPC_ERROR1, info.jre_version);
- exit(1);
- }
-
- 加载jre路径信息
- jre = LocateJRE(&info);
-
- //检查jre是否可使用,它会尝试启动jre并结束jre进程。
- ExecJRE(jre, new_argv);
ExecJRE
- //尝试启动jre
- if (!CreateProcess((LPCTSTR)path, /* executable name */
- (LPTSTR)cmdline, /* command line */
- (LPSECURITY_ATTRIBUTES)NULL, /* process security attr. */
- (LPSECURITY_ATTRIBUTES)NULL, /* thread security attr. */
- (BOOL)TRUE, /* inherits system handles */
- (DWORD)0, /* creation flags */
- (LPVOID)NULL, /* environment block */
- (LPCTSTR)NULL, /* current directory */
- (LPSTARTUPINFO)&si, /* (in) startup information */
- (LPPROCESS_INFORMATION)&pi)) { /* (out) process information */
- JLI_ReportErrorMessageSys(SYS_ERROR1, path);
- exit(1);
- }
-
- if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED) {
- if (GetExitCodeProcess(pi.hProcess, &exitCode) == FALSE)
- exitCode = 1;
- } else {
- JLI_ReportErrorMessage(SYS_ERROR2);
- exitCode = 1;
- }
-
- CloseHandle(pi.hThread);
- CloseHandle(pi.hProcess);
CreateExecutionEnvironment
识别32位和64位模式
检查虚拟机特性
配置jvm路径 (jvm.dll)
- CreateExecutionEnvironment(&argc, &argv,
- jrepath, sizeof(jrepath),
- jvmpath, sizeof(jvmpath),
- jvmcfg, sizeof(jvmcfg));
加载 jvm dll文件(jvm实现在dll文件中)
- if (!LoadJavaVM(jvmpath, &ifn)) {
- return(6);
- }
加载特定版本的vcrt运行库
加载jvm.dll
获取 CreateJavaVM函数指针,jvm初始化入口在 CreateJavaVM
- LoadMSVCRT();
-
- /* Load the Java VM DLL */
- if ((handle = LoadLibrary(jvmpath)) == 0) {
- JLI_ReportErrorMessage(DLL_ERROR4, (char *)jvmpath);
- return JNI_FALSE;
- }
-
- /* Now get the function addresses */
- ifn->CreateJavaVM =
- (void *)GetProcAddress(handle, "JNI_CreateJavaVM");
- ifn->GetDefaultJavaVMInitArgs =
- (void *)GetProcAddress(handle, "JNI_GetDefaultJavaVMInitArgs");
- if (ifn->CreateJavaVM == 0 || ifn->GetDefaultJavaVMInitArgs == 0) {
- JLI_ReportErrorMessage(JNI_ERROR1, (char *)jvmpath);
- return JNI_FALSE;
- }
- 配置jdk一些初始化配置参数
- /* 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();
JVMInit->ContinueInNewThread->JavaMain
ContinueInNewThread 只是创建一个线程并执行JavaMain函数。
JavaMain
- int JNICALL
- JavaMain(void * _args)
- {
- //参数拷贝
- JavaMainArgs *args = (JavaMainArgs *)_args;
- int argc = args->argc;
- char **argv = args->argv;
- int mode = args->mode;
- char *what = args->what;
- InvocationFunctions ifn = args->ifn;
-
- JavaVM *vm = 0; //虚拟机数据结构指针
- JNIEnv *env = 0; //虚拟机运行环境
- jclass mainClass = NULL;
- jclass appClass = NULL; // actual application class being launched
- jmethodID mainID;
- jobjectArray mainArgs;
- int ret = 0;
- jlong start, end;
-
- RegisterThread(); //windows平台是空函数
-
-
- //InitializeJVM 调用虚拟机真正的入口函数
- /**
- InitializeJVM->CreateJavaVM 完成虚拟机初始化及启动
- 初始化堆
- 全局数据结构(锁、GC子系统)
- 启动类库加载等等
- */
- start = CounterGet();
- if (!InitializeJVM(&vm, &env, &ifn)) {
- JLI_ReportErrorMessage(JVM_ERROR1);
- exit(1);
- }
-
- if (showSettings != NULL) {
- ShowSettings(env, showSettings);
- CHECK_EXCEPTION_LEAVE(1);
- }
-
- // java -version 输出版本信息后退出。
- if (printVersion || showVersion) {
- PrintJavaVersion(env, showVersion);
- CHECK_EXCEPTION_LEAVE(0);
- if (printVersion) {
- LEAVE();
- }
- }
-
- //没有指定jar或者入口类
- if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
- PrintUsage(env, printXUsage);
- CHECK_EXCEPTION_LEAVE(1);
- LEAVE();
- }
-
- FreeKnownVMs(); /* after last possible PrintUsage() */
-
- //启动虚拟机花了多长时间
- if (JLI_IsTraceLauncher()) {
- end = CounterGet();
- JLI_TraceLauncher("%ld micro seconds to InitializeJVM\n",
- (long)(jint)Counter2Micros(end-start));
- }
-
- //打印虚拟机环境参数
- if (JLI_IsTraceLauncher()){
- int i;
- printf("%s is '%s'\n", launchModeNames[mode], what);
- printf("App's argc is %d\n", argc);
- for (i=0; i < argc; i++) {
- printf(" argv[%2d] = '%s'\n", i, argv[i]);
- }
- }
-
- ret = 1;
-
- //加载入口类
- mainClass = LoadMainClass(env, mode, what);
- CHECK_EXCEPTION_NULL_LEAVE(mainClass);
-
- //JavaFX类应用使用,它没有main入口类。
- appClass = GetApplicationClass(env);
- NULL_CHECK_RETURN_VALUE(appClass, -1);
-
- //根据不同的java应用类初始化入口类
- PostJVMInit(env, appClass, vm);
-
- //获取 main方法ID
- mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
- "([Ljava/lang/String;)V");
- CHECK_EXCEPTION_NULL_LEAVE(mainID);
-
- //要传给main方法的参数
- mainArgs = CreateApplicationArgs(env, argv, argc);
- CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
-
- 调用main方法,我们的应用由此启动
- (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
-
- //main方法返回了
- ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
-
- //销毁虚拟机、清理资源(堆、线程、各个子系统)、退出进程、异常退出则需要dump内存
- LEAVE();
- }
虚拟机初始化及启动在 InitializeJVM 中,jvm初始化过程很比较复杂了,涉及到几乎所有的jvm子系统,后续继续分解它。
//InitializeJVM 调用虚拟机真正的入口函数
/**
InitializeJVM->CreateJavaVM 完成虚拟机初始化及启动
初始化堆
全局数据结构(锁、GC子系统)
启动类库加载等等
*/
由此我们的应用正式运行了
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);