本篇是JVM系列开篇,主要分析JVM的启动流程。JVM系列主要以HotSpot为主,按HotSpot各模块来一一展开,包括Class类文件成分,字节码文件加载及解析,HotSpot内存结构,JVM解析器,Kclass-Oop模型,垃圾回收器,执行引擎,字节码指令系统,JIT, AOT, 逃逸分析,栈顶缓存等等。
一. HotSpot的组成结构
JVM(全称Java Virtual Machine), 是一台虚拟的计算机,本身不跨平台。是Java,Python等编程语言生成的字节码文件的执行平台。具体的JVM可以由JRockit,HotSpot 等多种实现。但是需要遵循JVM规范,以实现语言的跨平台兼容性。最新的graalvm是一个多语言通用虚拟机。
HotSpot的组成包括:
1.类加载器子系统:负责加载二进制字节码文件流。
2.内存模型:Java栈,本地方法栈,Java堆,方法区,PC寄存器,直接内存
3.垃圾收集器:主要负责Java堆空间内存回收。
4.类文件解析器:解析二进制流字节码文件为对象模型
5.对象模型:虚拟机内部klass-Oop模型
6.解释器:虚拟机内部字节码执行单元,包括字节码解释器,模板解释器
7.编译器:虚拟机内置编译器,字节码指令的动态优化,包括JIT, AOT
8.监控: 虚拟机对外提供的运行时统计监控
9.运行时:虚拟机运行时环境
10.服务:虚拟机内部服务,包括内存,线程,运行时,类加载服务
11.os模块:负责与具体的寄生系统交互
二.HotSpot的启动流程
上一篇我们编译JDK, 和断点java命令,现在我们实际断点一个java Hello,打开Clion->Configurations
按如下配置:
jdk/src/java.base/share/native/launcher/main.c 这是java命令启动入口,在main()函数前打上断点
开始跟踪JVM的启动 main() -> JLI_Launch
1.初始化准备
jdk/src/java.base/share/native/libjli/java.c
断点到main函数末尾,调用JLI_Launch
//操作libjvm.so
typedef struct {
CreateJavaVM_t CreateJavaVM;
GetDefaultJavaVMInitArgs_t GetDefaultJavaVMInitArgs;
GetCreatedJavaVMs_t GetCreatedJavaVMs;
} InvocationFunctions;
//由main.c的main()函数调用
int JLI_Launch(int argc, char ** argv, /* main argc, argc */
int jargc, const char** jargv, /* java args */
......
)
{
......
InvocationFunctions ifn; //结构体
......
ifn.CreateJavaVM = 0;
ifn.GetDefaultJavaVMInitArgs = 0;
if (!LoadJavaVM(jvmpath, &ifn)) { //加载libjvm.so
return(6);
}
//启动属性设置
......
//初始化JVM
return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
}
jdk/src/java.base/unix/native/libjli/java_md_solinux.c
加载libjvm.so,绑定InvocationFunctions
jboolean LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn){ //
libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
ifn->CreateJavaVM = (CreateJavaVM_t) dlsym(libjvm, "JNI_CreateJavaVM");
ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t)
dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs");
ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t)
dlsym(libjvm, "JNI_GetCreatedJavaVMs");
return JNI_TRUE;
}
jdk/src/java.base/unix/native/libjli/java_md_solinux.c
初始化JVM
//调用 ContinueInNewThread
int JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
int argc, char **argv,
int mode, char *what, int ret)
{
ShowSplashScreen();
return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
}
jdk/src/java.base/share/native/libjli/java.c
创建一个新线程去创建JVM,调用JavaMain
ContinueInNewThread(InvocationFunctions* ifn, jlong threadStackSize,
int argc, char **argv,
int mode, char *what, int ret)
{
//设置线程栈大小
if (threadStackSize == 0) {
struct JDK1_1InitArgs args1_1;
memset((void*)&args1_1, 0, sizeof(args1_1));
args1_1.version = JNI_VERSION_1_1;
ifn->GetDefaultJavaVMInitArgs(&args1_1); /* ignore return value */
if (args1_1.javaStackSize > 0) {
threadStackSize = args1_1.javaStackSize;
}
}
{ /* Create a new thread to create JVM and invoke main method */
JavaMainArgs args;
int rslt;
args.argc = argc;
......
args.ifn = *ifn;
rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);
}
jdk/src/java.base/unix/native/libjli/java_md_solinux.c
int ContinueInNewThread0(int (JNICALL *continuation)(void *), jlong stack_size, void * args) {
int rslt;
#ifndef __solaris__
......
//创建线程
if (pthread_create(&tid, &attr, (void *(*)(void*))continuation, (void*)args) == 0) {
void * tmp;
pthread_join(tid, &tmp);
rslt = (int)(intptr_t)tmp;
} else {
//调用JavaMain
rslt = continuation(args);
}
......
#else /* __solaris__ */
......
return rslt;
jdk/src/java.base/share/native/libjli/java.c
执行JavaMain函数
int JNICALL JavaMain(void * _args) {
JavaMainArgs *args = (JavaMainArgs *)_args;
......
InvocationFunctions ifn = args->ifn;
//初始化虚拟机
start = CounterGet();
if (!InitializeJVM(&vm, &env, &ifn)) {
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
......
//加载Hello类
mainClass = LoadMainClass(env, mode, what);
appClass = GetApplicationClass(env);
//获取Hello类的main方法
mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
"([Ljava/lang/String;)V");
//调用main()方法
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
//结束
LEAVE();
}
//初始化据JVM
......
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;
r = ifn->CreateJavaVM(pvm, (void **)penv, &args); //进入到libjvm.so
JLI_MemFree(options);
return r == JNI_OK;
}
2.进入JVM
hotspot/src/share/vm/prims/jni.cpp
进入JVM初始化阶段,prims模块是外部与JVM交互模块
_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {
jint result = 0;
result = JNI_CreateJavaVM_inner(vm, penv, args); //调用(如下)
return result;
}
static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
HOTSPOT_JNI_CREATEJAVAVM_ENTRY((void **) vm, penv, args);
jint result = JNI_ERR;
......
//创建VM
result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
if (result == JNI_OK) {
JavaThread *thread = JavaThread::current();
/* thread is thread_in_vm here */
*vm = (JavaVM *)(&main_vm);
*(JNIEnv**)penv = thread->jni_environment();
......
//跟踪应用
RuntimeService::record_application_start();
......
return result;
}
3.初始化JVM
hotspot/src/share/vm/runtime/thread.cpp
执行初始化,主要分三个阶段:
第一阶段:HOTSPOT_VM_INIT_BEGIN -> HOTSPOT_VM_INIT_END
第二阶段:call_initPhase2
第三阶段:call_initPhase3
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
.....
//TLS初始化
ThreadLocalStorage::init();
// 输入输出流初始化
ostream_init();
//os模块初始化
os::init();
os::init_before_ergo();
//VM初始化开始
HOTSPOT_VM_INIT_BEGIN();
//初始化全局数据结构并在堆中创建系统类
vm_init_globals();
//Attach线程
JavaThread* main_thread = new JavaThread();
main_thread->set_thread_state(_thread_in_vm);
main_thread->initialize_thread_current();
// 线程栈基和栈大小
main_thread->record_stack_base_and_size();
main_thread->set_active_handles(JNIHandleBlock::allocate_block());
// Enable guard page *after* os::create_main_thread(), otherwise it would
// crash Linux VM, see notes in os_linux.cpp.
main_thread->create_stack_guard_pages();
//初始化Java级同步子系统
ObjectMonitor::Initialize();
// 初始化全局模块
jint status = init_globals();
// Should be done after the heap is fully created
main_thread->cache_global_variables();
//初始化java/lang下的类
initialize_java_lang_classes(main_thread, CHECK_JNI_ERR);
//标记初始化完成
set_init_completed();
//元空间初始化
Metaspace::post_initialize();
HOTSPOT_VM_INIT_END();
// 预先初始化一些JSR292核心类,String,System,Class类
initialize_jsr292_core_classes(CHECK_JNI_ERR);
// 这将初始化模块系统。只有java.base类可以是加载到阶段2完成
call_initPhase2(CHECK_JNI_ERR);
// 即使还没有JVMTI环境,也要始终调用,因为环境可能会延迟连接,而且JVMTI必须跟踪VM执行的各个阶段
JvmtiExport::enter_start_phase();
// Notify JVMTI agents that VM has started (JNI is up) - nop if no agents.
JvmtiExport::post_vm_start();
//最终系统初始化,包括安全管理器和系统类加载器
call_initPhase3(CHECK_JNI_ERR);
// 缓存系统类加载器
SystemDictionary::compute_java_system_loader(CHECK_(JNI_ERR));
//即使还没有JVMTI环境,也要始终调用,因为环境可能会延迟连接,而且JVMTI必须跟踪VM执行的各个阶段
JvmtiExport::enter_live_phase();
//通知初始化完成
JvmtiExport::post_vm_initialized();
......
#ifdef ASSERT
_vm_complete = true;
#endif
return JNI_OK; //JVM初始化完成
}
hotspot/src/share/vm/runtime/init.cpp
初始化全局数据结构并在堆中创建系统类
void vm_init_globals() {
check_ThreadShadow();
basic_types_init(); //heapOopSize与压缩指针
eventlog_init(); //一些事件
mutex_init();// 全局监视器和互斥体
chunkpool_init();//四个ChunkPool
perfMemory_init();//监控内存初始化
SuspendibleThreadSet_init();
}
hotspot/src/share/vm/runtime/init.cpp
初始化全局模块
jint init_globals() {
HandleMark hm;
management_init(); //监控服务,线程服务,运行时服务,类加载服务初始化
bytecodes_init(); // 字节码解释器初始化
classLoader_init1(); //类加载器初始化第一阶段:zip,jimage入口,设置启动类路径
compilationPolicy_init();//编译策略,根据编译等级确定从c1,c2编译器
codeCache_init();//代码缓存初始化
VM_Version_init();
os_init_globals();
stubRoutines_init1();//例程初始化第一阶段,例程:代码调用点
//堆空间,元空间,AOTLoader,SymbolTable,StringTable,G1收集器初始化
jint status = universe_init();
if (status != JNI_OK)
return status;
interpreter_init(); // 模板解释器初始化
invocationCounter_init(); //热点统计初始化
marksweep_init();//标记清除GC初始化
accessFlags_init();
templateTable_init();//模板表初始化
InterfaceSupport_init();
SharedRuntime::generate_stubs();//生成部分例程入口
universe2_init(); // vmSymbols,系统字典,预加载类,构建基本数据对象模型
referenceProcessor_init();//对象引用策略
jni_handles_init();//JNI引用句柄
#if INCLUDE_VM_STRUCTS
vmStructs_init(); //一些相关数据结构
#endif // INCLUDE_VM_STRUCTS
vtableStubs_init();//虚表例程初始化
InlineCacheBuffer_init();//内联缓冲初始化
compilerOracle_init();//Oracle相关的初始化
dependencyContext_init();//监控统计相关
if (!compileBroker_init()) {
return JNI_EINVAL;
}
VMRegImpl::set_regName(); //虚拟机寄存器初始化
if (!universe_post_init()) {//初始化必要的类,注册堆空间到内存服务
return JNI_ERR;
}
javaClasses_init(); //java基础类相关计算
stubRoutines_init2(); // 例程初始化第二阶段,模板解释器继承体系初始化
MethodHandles::generate_adapters();//方法与代码缓存点适配
return JNI_OK;
}
至此HotSpot虚拟机初始化完毕,将返回java.c的JavaMain函数继续往下加载Hello主类并找到Hello类的静态main方法并执行,执行完毕后LEAVE(),退出。在ContinueInNewThread0中创建2号线程来完成后续初始化。HotSpot的初始化过程中集中体现在init_globals()中各模块的初始化,从最基础的监控,服务,类加载器初始化为后续各阶段的初始化做准备。后续包括解释器,类加载器,堆空间,编译器,收集器等各模块逐一初始化为HotSpot的运行做好准备。
HotSpot的初始化启动过程就是上述这些,中间省略掉了一些以凸显主要流程。下一篇将从类加载器出发分析HotSpot的类加载机制。