通過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.LauncherHelper
的checkAndLoadMain()
方法檢查並加載主類過程:
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()方法