JVM进阶4--JVM启动过程源码流程分析

以openJDK8源码为基础,分析JVM启动过程,在openJDK中目录/openjdk/hotspot/src/share/tools/目录下有launcherjvm启动函数,而在openJDK8中没有启动函数,函数入口位于

/openjdk/jdk/src/share/bin/main.c

由于JVM运行环境不同,大致有windows有I及linux两种,因此JVM代码中有一部分是平台无关的,有一部分是平台特有的,有一部分代码为了共用,使用条件编译#ifdef来区分不同平台以便复用代码,这里为了便于研究,只研究linux平台中的启动过程代码。

JVM启动过程分析

JVM启动过程方法函数调用过程大致如下:

1.main入口:/openjdk/jdk/src/share/bin/main.c 

main方法调用JLI_Launch

2.JLI_Launch:/openjdk/jdk/src/share/bin/java.c

JLI_Launch方法中通过LoadJavaVM来使用JNI调用JVM库中的JNI_CreateJavaVM函数

JNI_CreateJavaVM    /openjdk/hotspot/src/share/vm/prims/jni.cpp

进而调用create_vm函数来实现系统初始化等一系列设置   

 /openjdk/hotspot/src/share/vm/runtime/thread.cpp

JLI_Launch最后调用JVMInit来完成JVM的初始化启动等

3.JVMInit:/openjdk/jdk/src/solaris/bin/java_md_solinux.c 

主要调用JavaMain方法

4.JavaMain: /openjdk/jdk/src/share/bin/java.c

作为java程序执行的主线程,加载主类,获取主方法,执行调用,获取返回值,异常处理,最终调用LEAVE方法

5.LEAVE: /openjdk/jdk/src/share/bin/java.c

实现虚拟机的销毁等一系列处理清除操作

create_vm是系统初始化的具体过程,JavaMain函数作为java程序执行的具体流程控制,这里具体大致学习一下,细节以后慢慢研究,先把大致运行流程搞清。

JavaMain函数分析

代码位置: /openjdk/jdk/src/share/bin/main.c

总体执行过程如下:

                                      

初始化虚拟机InitializeJVM

/* Initialize the virtual machine */
    start = CounterGet();
    if (!InitializeJVM(&vm, &env, &ifn)) {
        JLI_ReportErrorMessage(JVM_ERROR1);
        exit(1);
    }

其具体执行过程如下所示:

/*
 * Initializes the Java Virtual Machine. Also frees options array when
 * finished.
 */
static jboolean
InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn)
{
    JavaVMInitArgs args;
    jint r;

    memset(&args, 0, sizeof(args));
    args.version  = JNI_VERSION_1_2;
    args.nOptions = numOptions;
    args.options  = options;
    args.ignoreUnrecognized = JNI_FALSE;

    if (JLI_IsTraceLauncher()) {
        int i = 0;
        printf("JavaVM args:\n    ");
        printf("version 0x%08lx, ", (long)args.version);
        printf("ignoreUnrecognized is %s, ",
               args.ignoreUnrecognized ? "JNI_TRUE" : "JNI_FALSE");
        printf("nOptions is %ld\n", (long)args.nOptions);
        for (i = 0; i < numOptions; i++)
            printf("    option[%2d] = '%s'\n",
                   i, args.options[i].optionString);
    }

    r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
    JLI_MemFree(options);
    return r == JNI_OK;
}

初始化参数JavaVMInitArgs,调用JNI CreateJavaVM创建虚拟机,释放一部分参数内存资源

    r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
    JLI_MemFree(options);

参数解析,由于具体细节过于复杂,这里先大致了解一下,等到对于JVM源码有一定的了解之后,再回来慢慢研究。

JavaVM **pvm  由以下源码可知,JavaVM是一个结构体,含有许多对于虚拟机的操作函数

typedef JavaVM_ JavaVM; 

struct JavaVM_ {
    const struct JNIInvokeInterface_ *functions;

struct JNIInvokeInterface_ {
    void *reserved0;
    void *reserved1;
    void *reserved2;

    jint (JNICALL *DestroyJavaVM)(JavaVM *vm);

    jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args);

    jint (JNICALL *DetachCurrentThread)(JavaVM *vm);

    jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version);

    jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args);
};

JNIEnv **penv  JNI函数接口 集合

struct JNIEnv_ {
    const struct JNINativeInterface_ *functions;

 InvocationFunctions *ifn  含有3个JNI函数,创建VM,获取VM默认的初始化参数,获取已经创建的VMs

/*
 * Pointers to the needed JNI invocation API, initialized by LoadJavaVM.
 */
typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **env, void *args);
typedef jint (JNICALL *GetDefaultJavaVMInitArgs_t)(void *args);
typedef jint (JNICALL *GetCreatedJavaVMs_t)(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);

typedef struct {
    CreateJavaVM_t CreateJavaVM;
    GetDefaultJavaVMInitArgs_t GetDefaultJavaVMInitArgs;
    GetCreatedJavaVMs_t GetCreatedJavaVMs;
} InvocationFunctions;

加载主类LoadMainClass

LoadMainClass执行过程,主要是利用JNI方法来获取主类名称,进行类名处理,以及加载主类。

                                                                            

mainClass = LoadMainClass(env, mode, what);
/*
 * Loads a class and verifies that the main class is present and it is ok to
 * call it for more details refer to the java implementation.
 */
static jclass
LoadMainClass(JNIEnv *env, int mode, char *name)
{
    jmethodID mid;
    jstring str;
    jobject result;
    jlong start, end;
    jclass cls = GetLauncherHelperClass(env);
    NULL_CHECK0(cls);
    if (JLI_IsTraceLauncher()) {
        start = CounterGet();
    }
    NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
                "checkAndLoadMain",
                "(ZILjava/lang/String;)Ljava/lang/Class;"));

    str = NewPlatformString(env, name);
    CHECK_JNI_RETURN_0(
        result = (*env)->CallStaticObjectMethod(
            env, cls, mid, USE_STDERR, mode, str));

    if (JLI_IsTraceLauncher()) {
        end   = CounterGet();
        printf("%ld micro seconds to load main class\n",
               (long)(jint)Counter2Micros(end-start));
        printf("----%s----\n", JLDEBUG_ENV_ENTRY);
    }

    return (jclass)result;
}

获取launcherhelper类,类路径/openjdk/jdk/src/share/classes/sun/launcher/LauncherHelper.java

jclass
GetLauncherHelperClass(JNIEnv *env)
{
    if (helperClass == NULL) {
        NULL_CHECK0(helperClass = FindBootStrapClass(env,
                "sun/launcher/LauncherHelper"));
    }
    return helperClass;
}

获取到LauncherHelper后执行,checkAndLoadMain方法

NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
                "checkAndLoadMain",
                "(ZILjava/lang/String;)Ljava/lang/Class;"));

checkAndLoadMain方法如下所示:获取类名,加载该类,以及检查main方法参数等

/**
     * This method does the following:
     * 1. gets the classname from a Jar's manifest, if necessary
     * 2. loads the class using the System ClassLoader
     * 3. ensures the availability and accessibility of the main method,
     *    using signatureDiagnostic method.
     *    a. does the class exist
     *    b. is there a main
     *    c. is the main public
     *    d. is the main static
     *    e. does the main take a String array for args
     * 4. if no main method and if the class extends FX Application, then call
     *    on FXHelper to determine the main class to launch
     * 5. and off we go......
     *
     * @param printToStderr if set, all output will be routed to stderr
     * @param mode LaunchMode as determined by the arguments passed on the
     * command line
     * @param what either the jar file to launch or the main class when using
     * LM_CLASS mode
     * @return the application's main class
     */
    public static Class<?> checkAndLoadMain(boolean printToStderr,
                                            int mode,
                                            String what) {

获取GetApplicationClass

一些应用可能没有main方法,获取application main class

/*
     * In some cases when launching an application that needs a helper, e.g., a
     * JavaFX application with no main method, the mainClass will not be the
     * applications own main class but rather a helper class. To keep things
     * consistent in the UI we need to track and report the application main class.
     */
    appClass = GetApplicationClass(env);
    NULL_CHECK_RETURN_VALUE(appClass, -1);

从主类中获取main方法

mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                       "([Ljava/lang/String;)V");

调用main方法

/* Invoke main method. */
    (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);

函数退出

/*
     * The launcher's exit code (in the absence of calls to
     * System.exit) will be non-zero if main threw an exception.
     */
    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
    LEAVE();

最终JVM启动过程大致如下所示:

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值