转载

深入解析JVM源码 - 创建HotSpot

1 程序主入口

src/java.base/share/native/launcher/main.c
深入解析JVM源码 - 创建HotSpot

main函数返回了 JLI_Launch ()函数,位于

src/java.base/share/native/libjli/java.c

2 java.c # JLI_Launch()

JavaMain()是Java主程序的native调用。

在该方法里会执行虚拟机的初始化,获取Java程序主类及main方法,然后通过JNI调用main方法, 自此,整个JVM进程执行结束,最终退出。

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 ()方法

深入解析JVM源码 - 创建HotSpot

会执行一系列关于虚拟机的分配、挂载、初始化等工作,

且听下回分解

本文由博客一文多发平台 OpenWrite 发布!

原文  https://segmentfault.com/a/1190000020858422
正文到此结束
Loading...