美文网首页程序员
深入解析JVM源码 - 创建HotSpot

深入解析JVM源码 - 创建HotSpot

作者: JavaEdge | 来源:发表于2019-10-30 14:12 被阅读0次

    1 程序主入口

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

    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

        本文链接:https://www.haomeiwen.com/subject/uapovctx.html