深刻解析JVM源碼 - 建立HotSpot

1 程序主入口

src/java.base/share/native/launcher/main.c
java

main函數返回了JLI_Launch()函數,位於
src/java.base/share/native/libjli/java.csegmentfault

2 java.c # JLI_Launch()

JavaMain()是Java主程序的native調用。 數組

在該方法裏會執行虛擬機的初始化,獲取Java程序主類及main方法,而後經過JNI調用main方法, 自此,整個JVM進程執行結束,最終退出。app

int 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; // 實際啓動的應用程序類
    jmethodID mainID;
    jobjectArray mainArgs;
    int ret = 0;
    jlong start, end;

    RegisterThread();

    /* 初始化虛擬機 */
    start = CounterGet();
    if (!InitializeJVM(&vm, &env, &ifn)) {
        JLI_ReportErrorMessage(JVM_ERROR1);
        exit(1);
    }

    if (showSettings != NULL) {
        ShowSettings(env, showSettings);
        CHECK_EXCEPTION_LEAVE(1);
    }

    // 顯示已解決的模塊並繼續
    if (showResolvedModules) {
        ShowResolvedModules(env);
        CHECK_EXCEPTION_LEAVE(1);
    }

    // 列出可觀察的模塊,而後退出
    if (listModules) {
        ListModules(env);
        CHECK_EXCEPTION_LEAVE(1);
        LEAVE();
    }

    // 描述一個模塊,而後退出
    if (describeModule != NULL) {
        DescribeModule(env, describeModule);
        CHECK_EXCEPTION_LEAVE(1);
        LEAVE();
    }

    if (printVersion || showVersion) {
        PrintJavaVersion(env, showVersion);
        CHECK_EXCEPTION_LEAVE(0);
        if (printVersion) {
            LEAVE();
        }
    }

    // 模塊在啓動時已經過驗證,所以退出
    if (validateModules) {
        LEAVE();
    }

    /* 若是用戶未指定類名或JAR文件 */
    if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
        PrintUsage(env, printXUsage);
        CHECK_EXCEPTION_LEAVE(1);
        LEAVE();
    }

    FreeKnownVMs(); /* 最後一次可能的PrintUsage以後 */

    if (JLI_IsTraceLauncher()) {
        end = CounterGet();
        JLI_TraceLauncher("%ld micro seconds to InitializeJVM\n",
                          (long) (jint) Counter2Micros(end - start));
    }

    /* 在此階段,argc / argv具備應用程序的參數 */
    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;

    /*
     * 加載Java程序的main方法,若是沒找到則退出
     *
     * 獲取應用程序的主類. 它還檢查main方法是否存在
     * 請參見 bugid 5030265。已經從 manifest 中解析了 Main-Class 名稱,可是沒有爲UTF-8支持對其進行正確解析。
     * 所以,此處的代碼將忽略先前提取的值,並使用預先存在的代碼從新提取該值。
     * 這多是發佈週期權宜之計。
     * 可是,還發如今環境中傳遞某些字符集在Windows的某些變體中具備「奇怪」的行爲。
     * 所以,也許永遠都不該加強啓動器本地的清單解析代碼。
     * Hence the code here ignores the value previously extracted and
     * uses the pre-existing code to reextract the value.  This is
     * possibly an end of release cycle expedient.
     * Hence, maybe the manifest parsing code local to the
     * launcher should never be enhanced.
     *
     * 所以,將來的工做應:
     *     1)   更正本地解析代碼,並驗證Main-Class屬性是否已正確經過全部環境,
     *     2)   刪除經過環境維護 main_class 的方法(並刪除這些註釋).
     *
     * 此方法還能夠正確處理啓動可能具備或不具備Main-Class清單條目的現有JavaFX應用程序.
     */
    mainClass = LoadMainClass(env, mode, what);
    CHECK_EXCEPTION_NULL_LEAVE(mainClass);
    /*
     * 獲取程序主類Class對象
     *
     * 在某些狀況下,當啓動 須要幫助程序的 應用程序(例如,沒有main方法的JavaFX應用程序)時,
     * mainClass將不是應用程序本身的主類,而是幫助程序類。
     * 爲了使UI中的內容保持一致,咱們須要跟蹤和報告應用程序主類。
     */
    appClass = GetApplicationClass(env);
    NULL_CHECK_RETURN_VALUE(appClass, -1);

    /* 構建平臺特定的參數數組(構建main方法的參數列表) */
    mainArgs = CreateApplicationArgs(env, argv, argc);
    CHECK_EXCEPTION_NULL_LEAVE(mainArgs);

    if (dryRun) {
        ret = 0;
        LEAVE();
    }

    /*
     * PostJVMInit 使用類名稱做爲用於GUI的應用程序名稱
     * 例如, 在 OSX 上, 這會在菜單欄中爲SWT和JavaFX設置應用程序名稱.
     * 所以, 咱們將在此處傳遞實際的應用程序類而不是mainClass, 由於這多是啓動器或幫助程序類, 而不是應用程序類.
     */
    PostJVMInit(env, appClass, vm);
    CHECK_EXCEPTION_LEAVE(1);

    /*
     * 獲取main方法ID
     *
     * LoadMainClass不只加載主類,還將確保主方法的簽名正確,這樣就不須要再進一步檢查了.
     * 這裏調用main方法,以便無關的Java堆棧不在應用程序stack trace中.
     */
    mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                       "([Ljava/lang/String;)V");
    CHECK_EXCEPTION_NULL_LEAVE(mainID);

    /* 調用main方法. */
    (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);

    /*
     * 若是main拋出異常,則啓動程序的退出碼(在沒有對System.exit的調用的狀況下)將爲非零。
     */
    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;

    LEAVE();
}

該方法中調用的InitializeJVM()方法

會執行一系列關於虛擬機的分配、掛載、初始化等工做,
且聽下回分解函數

本文由博客一文多發平臺 OpenWrite 發佈!
相關文章
相關標籤/搜索