基于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);