OpenJDK8源码系列01-JVM生命周期源码概览
1. 引言
JVM(Java Virtual Machine)是 Java 程序的运行基石。理解 JVM 的生命周期,即从启动到销毁的完整过程,是深入掌握 Java 运行时机制的第一步。本文将从 OpenJDK8 源码出发,概述在linux系统JVM 的启动、初始化、执行与销毁四个核心阶段的调用链,帮助读者建立对 JVM 整体架构的宏观认知。
2. JVM 启动入口
JVM 的启动入口位于src/share/bin/main.c文件中的main()函数。
// src/share/bin/main.cintmain(intargc,char**argv){...returnJLI_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);}进入main函数,JVM进程开始运行。该函数相对简单,调用了JLI_Launch函数
// src/share/bin/java.c/* * Entry point. */intJLI_Launch(intargc,char**argv,/* main argc, argc */intjargc,constchar**jargv,/* java args */intappclassc,constchar**appclassv,/* app classpath */constchar*fullversion,/* full version defined */constchar*dotversion,/* dot version defined */constchar*pname,/* program name */constchar*lname,/* launcher name */jboolean javaargs,/* JAVA_ARGS */jboolean cpwildcard,/* classpath wildcard*/jboolean javaw,/* windows-only javaw */jint ergo/* ergonomics class policy */){...CreateExecutionEnvironment(&argc,&argv,jrepath,sizeof(jrepath),jvmpath,sizeof(jvmpath),jvmcfg,sizeof(jvmcfg));ifn.CreateJavaVM=0;ifn.GetDefaultJavaVMInitArgs=0;...if(!LoadJavaVM(jvmpath,&ifn)){return(6);}...returnJVMInit(&ifn,threadStackSize,argc,argv,mode,what,ret);}JLI_Launch函数需要重点关注的是调用了CreateExecutionEnvironment、LoadJavaVM和JVMInit等函数。
CreateExecutionEnvironment函数创建运行环境
LoadJavaVM函数主要是加载JVM共享链接库libvjm.so,将函数指针ifn->CreateJavaVM指向JNI_CreateJavaVM,ifn->GetDefaultJavaVMInitArgs指向JNI_GetDefaultJavaVMInitArgs,ifn->GetCreatedJavaVMs指向JNI_GetCreatedJavaVMs。
// src/solaris/bin/java_md_solinux.cintJVMInit(InvocationFunctions*ifn,jlong threadStackSize,intargc,char**argv,intmode,char*what,intret){...returnContinueInNewThread(ifn,threadStackSize,argc,argv,mode,what,ret);}JVMInit调用了ContinueInNewThread函数去开启新线程。
// src/share/bin/java.cintContinueInNewThread(InvocationFunctions*ifn,jlong threadStackSize,intargc,char**argv,intmode,char*what,intret){...rslt=ContinueInNewThread0(JavaMain,threadStackSize,(void*)&args);return(ret!=0)?ret:rslt;}}ContinueInNewThread函数调用了ContinueInNewThread0函数去开启新线程。
如果成功创建新线程,由新线程调用JavaMain函数。到目前为止,在linux内核角度来看,linux内核已经为该JVM进程创建了两个TASK_STRUCT.。一个是之前main函数所在的TASK_STRUCT暂且叫它A,一个是新线程所在的TASK_STRUCT暂且叫它B。A关联的线程会一直等待B关联的线程运行完再继续运行。
如果创建新线程失败,则由原有的线程调用JavaMain函数。到目前为止,在linux内核角度来看,linux内核已经为该JVM进程创建了一个TASK_STRUCT.
// src/share/bin/java.cintJNICALLJavaMain(void*_args){...if(!InitializeJVM(&vm,&env,&ifn)){JLI_ReportErrorMessage(JVM_ERROR1);exit(1);}...mainClass=LoadMainClass(env,mode,what);..mainID=(*env)->GetStaticMethodID(env,mainClass,"main","([Ljava/lang/String;)V");...mainArgs=CreateApplicationArgs(env,argv,argc);...(*env)->CallStaticVoidMethod(env,mainClass,mainID,mainArgs);..LEAVE();}JavaMain函数中调用了InitializeJVM、LoadMainClass、(*env)->GetStaticMethodID、CreateApplicationArgs、 (*env)->CallStaticVoidMethod等关键函数和宏LEAVE().
InitializeJVM函数极为重要,做了很多事情,初始化ClassLoader,创建了堆并进行初始化,创建垃圾收集器和相应的垃圾收集策略,后面在详解InitializeJVM再详细展开。
LoadMainClass函数找到并加载JAVA应用的Main Class.
(*env)->GetStaticMethodID获取JAVA应用Main Class的main方法ID。
CreateApplicationArgs函数创建JAVA应用Main Class main方法的参数。
(*env)->CallStaticVoidMethod函数开始调用Java应用Main Class的main方法,这里开始运行JAVA程序代码。
宏LEAVE()在JAVA应用程序执行完后对JVM进行销毁。
至此,JVM生命周期源码概览结束。
