美文网首页
JVM启动过程源码分析

JVM启动过程源码分析

作者: 快给我饭吃 | 来源:发表于2019-06-15 15:18 被阅读0次

    从开始接触java写第一个hello world程序时,我们就知道,编译完成后只要在命令行里输入java yourClassFilename这个java命令,就能启动jvm虚拟机运行你的java程序。但是操作系统到底怎么启动jvm虚拟机的,那就要分析分析jdk的源码了。

    启动步骤

    从openjdk官网下载源码进行分析,jvm启动过程分为两部分:launch启动器和hotspot vm自身启动。大概步骤如下。

    • 准备工作,包括查找jre、jvm
    • 装载动态链接库
    • 解析jvm参数
    • 启动虚拟机
    • 加载主类,并调用主类的main()方法

    源码分析

    c++程序的入口点,自然是唯一的main函数,main函数在openjdk/jdk/src/share/bin/main.c文件中,其调用了JLI_Launch启动,代码在openjdk/jdk/src/share/bin/java.c中。

    main(int argc, char **argv)
    {
        int margc;
        char** margv;
        const jboolean const_javaw = JNI_FALSE; //是否以javaw启动
        margc = argc;
        margv = argv;
        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方法代码如下,整个jvm启动逻辑都在这里了。首先SelectVersion()方法确定系统安装了你指定的JRE版本,接着CreateExecutionEnvironment

    /*
     * 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 */
    )
    {
        //启动模式,如果非jar,非main class启动,那么程序将退出
        int mode = LM_UNKNOWN;
        //存储启动类名
        char *what = NULL;
        //储存classpath
        char *cpath = 0;
        //主类引用
        char *main_class = NULL;
        int ret;
        //加载JAVA_DLL时,将一些函数链接到此变量中
        InvocationFunctions ifn;
        jlong start, end;
        //jvm路径
        char jvmpath[MAXPATHLEN];
        //jre路径
        char jrepath[MAXPATHLEN];
        //jvm.cfg路径
        char jvmcfg[MAXPATHLEN];
    
        _fVersion = fullversion;
        _dVersion = dotversion;
        _launcher_name = lname;
        _program_name = pname;
        _is_java_args = javaargs;
        _wc_enabled = cpwildcard;
        _ergo_policy = ergo;
    
        //根据环境变量中的“JLDEBUG_ENV_ENTRY”参数来设置_launcher_debug,表示是否打印调试信息
        /*
         * Initialize platform specific settings
         */
        InitLauncher(javaw);
        //printf一些调试的提示,无其他作用
        DumpState();
        //判断_launcher_debug输出调试信息
        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);
        }
    
        /*
         * Make sure the specified version of the JRE is running.
         *
         * There are three things to note about the SelectVersion() routine:
         *  1) If the version running isn't correct, this routine doesn't
         *     return (either the correct version has been exec'd or an error
         *     was issued).
         *  2) Argc and Argv in this scope are *not* altered by this routine.
         *     It is the responsibility of subsequent code to ignore the
         *     arguments handled by this routine.
         *  3) As a side-effect, the variable "main_class" is guaranteed to
         *     be set (if it should ever be set).  This isn't exactly the
         *     poster child for structured programming, but it is a small
         *     price to pay for not processing a jar file operand twice.
         *     (Note: This side effect has been disabled.  See comment on
         *     bugid 5030265 below.);ll;;
         */
        // jre相关,从命令行-version:或从Mainfest获取jre版本,并使用LocateJRE()从注册表键值JavaHome定位到jre路径,最后使用ExecJRE()小启动一把jre
        SelectVersion(argc, argv, &main_class);
    
        //准备环境,先根据java.exe目录查找jdk的jre路径,称为private jre,取不到则取注册表的jre路径,称为public jre。然后查找到jre/bin/java.dll为java虚拟机。查找jre/lib/amd64/jvm.cfg文件,获取虚拟机启动类型。根据指定虚拟机类型如server,查找jre/bin/server/jvm.dll虚拟机文件。
        CreateExecutionEnvironment(&argc, &argv,
                                   jrepath, sizeof(jrepath),
                                   jvmpath, sizeof(jvmpath),
                                   jvmcfg,  sizeof(jvmcfg));
    
        ifn.CreateJavaVM = 0;
        ifn.GetDefaultJavaVMInitArgs = 0;
    
        if (JLI_IsTraceLauncher()) {
            start = CounterGet();
        }
        //加载jvm.dll到内存,并将动态库中的函数链接至ifn.CreateJavaVM,ifn.GetDefaultJavaVMInitArgs
        if (!LoadJavaVM(jvmpath, &ifn)) {
            return(6);
        }
      
        if (JLI_IsTraceLauncher()) {
            end   = CounterGet();
        }
    
        JLI_TraceLauncher("%ld micro seconds to LoadJavaVM\n",
                 (long)(jint)Counter2Micros(end-start));
    
        ++argv;
        --argc;
    
        if (IsJavaArgs()) {
            /* Preprocess wrapper arguments */
            TranslateApplicationArgs(jargc, jargv, &argc, &argv);
            if (!AddApplicationOptions(appclassc, appclassv)) {
                return(1);
            }
        } else {
            /* Set default CLASSPATH */
            cpath = getenv("CLASSPATH");
            if (cpath == NULL) {
                cpath = ".";
            }
    
            //设置CLASSPATH,并设置虚拟机参数Xms, Xmx, Xss
            SetClassPath(cpath);
        }
    
        /* Parse command line options; if the return value of
         * ParseArguments is false, the program should exit.
         */
        //解析所有option参数,并将其存到JavaVMOption *options变量中
        if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath))
        {
            return(ret);
        }
    
        /* Override class path if -jar flag was specified */
        if (mode == LM_JAR) {
            SetClassPath(what);     /* Override class path */
        }
    
        /* 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();
        
        //展示splash screen,开启新线程去初始化JVM,最终调用jvm.dll中的CreateJavaVM启动虚拟机。并通过LoadMainClass()方法获取main class,找到bootstrap类加载器加载sun/launcher/LauncherHelper,执行该类的checkAndLoadMain方法,初始化ExtClassLoader和AppClassLoader类加载器,执行sun.misc.Launcher.getLauncher()的getClassLoader()获取AppClassLoader加载主类,调用main class的main方法
        return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
    }
    

    总结的不是很好,有空我再多写几篇。

    相关文章

      网友评论

          本文标题:JVM启动过程源码分析

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