JVM啓動分析

通過openjdk的源碼來分析jvm的啓動過程,針對linux系統。

jvm的啓動入口main():

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

/**
 * main方法
 *
 * argc 參數個數
 * argv 參數數組
 */
int main(int argc, char **argv) {
    return JLI_Launch(...);
}

JLI_Launch()方法:

// openjdk7u/jdk/src/share/bin/java.c

int JLI_Launch(...) {
    // 啓動初始化
    InitLauncher(javaw);

    // 選擇jre版本
    SelectVersion(argc, argv, &main_class);

    // 創建執行環境
    CreateExecutionEnvironment(...);

    // 加載Java虛擬機
    if (!LoadJavaVM(jvmpath, &ifn)) {
        return (6);
    }

    if (IsJavaArgs()) {
        TranslateApplicationArgs(jargc, jargv, &argc, &argv);
        if (!AddApplicationOptions(appclassc, appclassv)) {
            return (1);
        }
    } else {
        // 讀取環境變量CLASSPATH
        cpath = getenv("CLASSPATH");
        if (cpath == NULL) {
            cpath = ".";
        }
        // 設置類路徑
        SetClassPath(cpath);
    }

    // 解析命令行參數
    if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath)) {
        return (ret);
    }

    if (mode == LM_JAR) {
        // jar啓動模式, 重新設置類路徑
        SetClassPath(what);
    }

    SetJavaCommandLineProp(what, argc, argv);

    SetJavaLauncherProp();

    SetJavaLauncherPlatformProps();

    return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
}

InitLauncher()方法:

// openjdk7u/jdk/src/solaris/bin/java_md_common.c

void InitLauncher(jboolean javaw) {
    // 讀取環境變量_JAVA_LAUNCHER_DEBUG, 來設置是否打印debug信息
    JLI_SetTraceLauncher();
}

JLI_SetTraceLauncher()方法:

// openjdk7u/jdk/src/share/bin/jli_util.c

// 是否開啓debug模式
static jboolean _launcher_debug = JNI_FALSE;

void JLI_SetTraceLauncher() {
    if (getenv(JLDEBUG_ENV_ENTRY) != 0) {
        _launcher_debug = JNI_TRUE;
        JLI_TraceLauncher("----%s----\n", JLDEBUG_ENV_ENTRY);
    }
}

jboolean JLI_IsTraceLauncher() {
    return _launcher_debug;
}

void JLI_TraceLauncher(const char *fmt, ...) {
    if (_launcher_debug != JNI_TRUE) return;
    vprintf(fmt, vl);
}

後續會調用JLI_IsTraceLauncher()JLI_TraceLauncher()方法輸出debug信息。

LoadJavaVM()方法:

// openjdk7u/jdk/src/solaris/bin/java_md_solinux.c

jboolean LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) {
    // 裝載動態鏈接庫, jre/lib/libjvm.so
    libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
    // 導出動態鏈接庫函數JNI_CreateJavaVM, 掛載到ifn
    ifn->CreateJavaVM = (CreateJavaVM_t) dlsym(libjvm, "JNI_CreateJavaVM");
    // 導出動態鏈接庫函數JNI_GetDefaultJavaVMInitArgs, 掛載到ifn
    ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t) dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs");
    // 導出動態鏈接庫函數JNI_GetCreatedJavaVMs, 掛載到ifn
    ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t) dlsym(libjvm, "JNI_GetCreatedJavaVMs");
    return JNI_TRUE;
}

JVMInit()方法:

// openjdk7u/jdk/src/solaris/bin/java_md_solinux.c

int JVMInit(...) {
    ShowSplashScreen();
    return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
}

ContinueInNewThread()方法:

// openjdk7u/jdk/src/share/bin/java.c

int ContinueInNewThread(InvocationFunctions *ifn, jlong threadStackSize, ...) {
    {
        //創建一個新的線程來執行JavaMain方法
        rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void *) &args);
    }
}

JavaMain()方法:

// openjdk7u/jdk/src/share/bin/java.c

int JNICALL JavaMain(void *_args) {
    // 初始化虛擬機
    if (!InitializeJVM(&vm, &env, &ifn)) {
        exit(1);
    }

    // 加載主類
    mainClass = LoadMainClass(env, mode, what);

    // 獲取主類的main()方法id
    mainID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V");

    // 創建main方法參數
    mainArgs = CreateApplicationArgs(env, argv, argc);

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

    // main方法退出
    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;

    // 銷燬虛擬機
    LEAVE();
}

static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn) {
    r = ifn->CreateJavaVM(pvm, (void **) penv, &args);
    return r == JNI_OK;
}

static jclass LoadMainClass(JNIEnv *env, int mode, char *name) {
    // 獲取sun.launcher.LauncherHelper類
    jclass cls = GetLauncherHelperClass(env);

    // 獲取LauncherHelper類的checkAndLoadMain()方法id
    mid = (*env)->GetStaticMethodID(env, cls, "checkAndLoadMain", "(ZILjava/lang/String;)Ljava/lang/Class;");

    // 調用LauncherHelper類的checkAndLoadMain()方法檢查並加載主類
    result = (*env)->CallStaticObjectMethod(env, cls, mid, USE_STDERR, mode, str);

    return (jclass) result;
}

jclass GetLauncherHelperClass(JNIEnv *env) {
    return FindBootStrapClass(env, "sun/launcher/LauncherHelper");
}

InitializeJVM()方法調用前面掛載的CreateJavaVM()方法,也就是JNI_CreateJavaVM()方法。

sun.launcher.LauncherHelpercheckAndLoadMain()方法檢查並加載主類過程:

public enum LauncherHelper {

    private static final int LM_UNKNOWN = 0;
    private static final int LM_CLASS = 1;
    private static final int LM_JAR = 2;

    /**
     * 檢查並加載主類
     *
     * @param useStdErr 是否使用標準錯誤輸出
     * @param mode      模式, 對應LM_UNKNOWN、LM_CLASS、LM_JAR
     * @param name      主類名
     * @return          主類Class
     */
    public static Class<?> checkAndLoadMain(boolean useStdErr, int mode, String name) {
        PrintStream printStream = useStdErr ? System.err : System.out;
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        String className = null;
        switch (mode) {
            // class啓動模式
            case 1:
                className = name;
                break;
            // jar啓動模式
            case 2:
                // 通過jar包中MANIFEST.MF文件的Main-Class獲取主類名
                className = getMainClassFromJar(printStream, name);
                break;
            default:
                throw new InternalError(mode + ": Unknown launch mode");
        }

        className = className.replace('/', '.');

        Class mainClass = null;
        try {
            // 加載主類
            mainClass = classLoader.loadClass(className);
        } catch (ClassNotFoundException var8) {
            // 主類加載失敗, 退出
            abort(printStream, var8, "java.launcher.cls.error1", new Object[]{className});
        }

        // 檢查主類的main()方法, public static void main([Ljava.lang.String)
        getMainMethod(printStream, mainClass);
        return mainClass;
    }

}

jvm的啓動模式:

  • jar啓動模式: java -jar main.jar
  • class啓動模式: java org.txazo.test.Main

jvm的整體啓動流程:

  • 準備工作,包括查找jre、jvm
  • 裝載動態鏈接庫
  • 解析jvm參數
  • 啓動虛擬機
  • 加載主類,並調用主類的main()方法
http://www.txazo.com/jvm/jvm-startup.html
發佈了6 篇原創文章 · 獲贊 18 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章