openjdk-jdk8-b109 hotspot虚拟机启动过程分析_1

基于OpenJdk 标签 jdk8-b109版本分析

HotSpot是由Oracle开发的Java虚拟机(JVM)的一种实现,也是使用最广泛的JVM之一。

找到hotspot虚拟机入口

        完成编译配置后,会在根目录生成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, \

进入jvm前-参数预处理

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

JLI_Launch

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

你可能感兴趣的:(Java,java,hotspot虚拟机,jvm启动过程)