深入剖析创建Java虚拟机的实现方法
经过前文《深入剖析java.c文件中JavaMain方法中InitializeJVM的实现》的分析,找到了创建Java虚拟机具体实现的方法Threads::create_vm((JavaVMInitArgs*) args, &can_try_again)。该方法的实现在src\hotspot\share\runtime\threads.cpp文件,我去掉了部分英文注释和宏条件代码,代码更简洁清晰,下面我们看看该方法的具体细节:
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // JDK版本信息初始化前的准备工作 extern void JDK_Version_init(); VM_Version::early_initialize(); // 检查版本是否支持 if (!is_supported_jni_version(args->version)) return JNI_EVERSION; // 初始化基于库的TLS(线程本地存储) ThreadLocalStorage::init(); // 初始化输出流模块 ostream_init(); // 处理Java启动器属性 Arguments::process_sun_java_launcher_properties(args); // 初始化操作系统模块,其中包括:获取进程ID、初始化系统信息、 // 设置流模式、设置虚拟内存页大小、初始化主进程句柄和主线程句柄等 os::init(); //该代码的作用是在MacOS AARCH64架构下启用当前线程的写执行保护 MACOS_AARCH64_ONLY(os::current_thread_enable_wx(WXWrite)); // 创建TraceVmCreationTime对象create_vm_timer,开始统计虚拟机创建时间 TraceVmCreationTime create_vm_timer; create_vm_timer.start(); // 初始化系统属性 Arguments::init_system_properties(); // 初始化JDK版本,以便在解析参数时使用 JDK_Version_init(); // 在JDK版本号已知后,更新或初始化系统属性 Arguments::init_version_specific_system_properties(); // 在解析参数之前,确保初始化日志配置 LogConfiguration::initialize(create_vm_timer.begin_time()); // 解析参数 jint parse_result = Arguments::parse(args); if (parse_result != JNI_OK) return parse_result; // 初始化内存跟踪 MemTracker::initialize(); // 初始化初始的活动处理器数量、大页面支持、栈溢出大小和VM版本 os::init_before_ergo(); // 设置堆大小,初始化元空间标志和对齐,设置编译器标志和 // 字节码重写标志,以及根据激进优化标志设置标志 jint ergo_result = Arguments::apply_ergo(); if (ergo_result != JNI_OK) return ergo_result; // 检查上述init_before_ergo()和apply_ergo()对JVM的设置 if (!JVMFlagLimit::check_all_ranges()) { return JNI_EINVAL; } // 检查上述init_before_ergo()和apply_ergo()对JVM的约束设置 bool constraint_result = JVMFlagLimit::check_all_constraints( JVMFlagConstraintPhase::AfterErgo); if (!constraint_result) { return JNI_EINVAL; } // 如果上述检查有问题,则暂停虚拟机初始化 if (PauseAtStartup) { os::pause(); } // 正式开始虚拟机的初始化 HOTSPOT_VM_INIT_BEGIN(); // 使用TraceTime计时器记录“Create VM”这个操作的时间,并将时间记录 // 在TRACETIME_LOG中的Info级别的startuptime日志中 TraceTime timer("Create VM", TRACETIME_LOG(Info, startuptime)); // 在全局参数解析后,初始化一些操作系统相关的设置, // 包括设置互斥锁初始化完成标志、配置Windows异常处理、 // 检查和设置最小堆栈大小、注册退出函数等 jint os_init_2_result = os::init_2(); if (os_init_2_result != JNI_OK) return os_init_2_result; // 初始化安全点机制 SafepointMechanism::initialize(); // 根据操作系统的结果进行调整 jint adjust_after_os_result = Arguments::adjust_after_os(); if (adjust_after_os_result != JNI_OK) return adjust_after_os_result; // 初始化输出流日志 ostream_init_log(); // 加载 -agentlib/-agentpath 和转化后的 -Xrun agents JvmtiAgentList::load_agents(); // 初始化线程状态 _number_of_threads = 0; _number_of_non_daemon_threads = 0; // 初始化虚拟机的全局变量,包括:基本类型、事件日志、 // 互斥锁、面向对象存储集、性能内存、可挂起线程集合等 vm_init_globals(); // 初始化Java线程的旧计数器,如果JVMCI计数器大小大于0, // 则会分配一个新的C堆数组来存储旧计数器,并将其初始化为0。 // 否则,将JavaThread::_jvmci_old_thread_counters设置为nullptr。 // 这段代码主要是为了支持JVMCI,该技术可以在运行时优化Java应用程序 #if INCLUDE_JVMCI if (JVMCICounterSize > 0) { JavaThread::_jvmci_old_thread_counters = NEW_C_HEAP_ARRAY(jlong, JVMCICounterSize, mtJVMCI); memset(JavaThread::_jvmci_old_thread_counters, 0, sizeof(jlong) * JVMCICounterSize); } else { JavaThread::_jvmci_old_thread_counters = nullptr; } #endif // INCLUDE_JVMCI // 给当前线程对象初始化面向对象存储集 JavaThread::_thread_oop_storage = OopStorageSet::create_strong( "Thread OopStorage", mtThread); // 创建一个JVM主线程,并将它挂载到当前操作系统线程上 JavaThread* main_thread = new JavaThread(); // 设置了线程的状态 main_thread->set_thread_state(_thread_in_vm); // 初始化JVM主线程 main_thread->initialize_thread_current(); // 记录了线程的栈基址和大小 before set_active_handles main_thread->record_stack_base_and_size(); // 将线程栈注册到NMT中 main_thread->register_thread_stack_with_NMT(); // 分配了一个JNIHandleBlock对象,为线程设置活动句柄 main_thread->set_active_handles(JNIHandleBlock::allocate_block()); // 在特定平台上对当前线程启用写保护 MACOS_AARCH64_ONLY(main_thread->init_wx()); // 以下if代码作用是:检查主线程是否被设置为起始线程, // 如果没有成功设置,则会发生内部分配失败,导致虚拟机初始化失败, // 此时会关闭虚拟机并返回错误代码JNI_ENOMEM if (!main_thread->set_as_starting_thread()) { vm_shutdown_during_initialization("Failed necessary internal allocation. Out of swap space"); main_thread->smr_delete(); *canTryAgain = false; return JNI_ENOMEM; } // 在主线程创建后启用保护页,以避免Linux VM 崩溃 main_thread->stack_overflow_state()->create_stack_guard_pages(); // 初始化Java级别的同步子系统。首先调用ObjectMonitor类的 // Initialize方法来初始化监视器对象,然后调用ObjectSynchronizer // 类的initialize方法来初始化对象同步器。这些步骤是确保Java程序 // 中的多线程同步操作能够正确地执行 ObjectMonitor::Initialize(); ObjectSynchronizer::initialize(); // 初始化全局变量,包括:管理模块、对象存储、字节码、引导类加载器、 // 编译策略、代码缓存、虚拟机版本、存根、Java内存模型相关的组件 // (比如、TLAB、堆、元空间、方法缓存、方法表、符号表、字符串表、 // JVM内部数据结构等)以及GC日志、异步日志、垃圾回收栅栏、协程、 // 协程存根、解释器存根、访问标识、接口支持、寄存器名称、共享 // 运行时生成存根 jint status = init_globals(); if (status != JNI_OK) { // 删除主线程 main_thread->smr_delete(); // 将canTryAgain指针指向false,防止调用者再次调用 // JNI_CreateJavaVM函数 *canTryAgain = false; return status; } // 将主线程添加到线程列表中,以完成栅栏的设置, // 并在init_globals2中构建Java对象之前进on_thread_attach。 // 该代码通过使用MutexLocker锁定线程锁Threads_lock, // 然后将主线程添加到线程列表中 { MutexLocker mu(Threads_lock); Threads::add(main_thread); } // 初始化一些全局变量 status = init_globals2(); if (status != JNI_OK) { // 从线程列表中移除指定的线程 Threads::remove(main_thread, false); main_thread->smr_delete(); *canTryAgain = false; return status; } // 这段代码的作用是仅在JFR(Java Flight Recorder)启用时 // 调用Jfr::on_create_vm_1()函数 JFR_ONLY(Jfr::on_create_vm_1();) // 在堆完全创建后运行,主线程将缓存全局变量 main_thread->cache_global_variables(); // 在onload中进入的任何JVMTI原始监视器都将转换为真实的原始 // 监视器,在这里,VM已经设置好了,可以进入原始监视器 JvmtiExport::transition_pending_onload_raw_monitors(); // 以下代码的作用是创建一个虚拟机线程并等待它初始化完成 { TraceTime timer("Start VMThread", TRACETIME_LOG(Info, startuptime)); VMThread::create(); VMThread* vmthread = VMThread::vm_thread(); if (!os::create_thread(vmthread, os::vm_thread)) { vm_exit_during_initialization("Cannot create VM thread. " "Out of system resources."); } { // 使用MonitorLocker和Notify_lock来等待VM线程初始化完成 MonitorLocker ml(Notify_lock); os::start_thread(vmthread); while (!vmthread->is_running()) { ml.wait(); } } } // Java内存模型及JVM核心组件是否初始化完成,确保我们以干净的状态开始 assert(Universe::is_fully_initialized(), "not initialized"); if (VerifyDuringStartup) { VM_Verify verify_op; // 启动一个新线程,执行验证操作 VMThread::execute(&verify_op); } // 这段代码的作用是更新java.vm.info属性,以防初始定义 // 它的任何标志已被更改。 // 这对于CDS非常重要,因为UseSharedSpaces可能在java.vm.info // 最初计算之后被更改。在我们初始化java类之前,但在可能修改 // 标志的任何初始化逻辑之后,必须进行此更新。 // CDS是Class Data Sharing的缩写,是一种JVM的优化技术。 // 它可以将类数据预处理并保存在共享的归档文件中,以便在后续 // 的JVM启动中重用这些数据,从而加快JVM的启动时间 和内存占用。 // CDS在JDK 5中首次引入,JDK 8中进行了进一步的改进。 Arguments::update_vm_info_property(VM_Version::vm_info_string()); // 创建一个Java线程,并使用异常宏THREAD进行异常处理 JavaThread* THREAD = JavaThread::current(); // For exception macros. // 创建一个HandleMark对象,该对象在当前线程上下文中创建一个 // 句柄标记。这将确保在当前线程上下文中创建的所有句柄都被正确 // 处理,并在当前线程上下文结束时自动清除 HandleMark hm(THREAD); // 调用JvmtiExport的enter_early_start_phase()函数,即使还没有 // JVMTI环境,因为环境可能会晚些时候附加,JVMTI必须跟踪VM执行 // 的阶段,JvmtiExport是一个JVMTI API的导出类,其中包含了 // 许多用于与JVMTI代理交互的函数。 JvmtiExport::enter_early_start_phase(); // 通知JVMTI代理VM已经启动(JNI已经启动),如果没有代理则 // 不执行任何操作 JvmtiExport::post_early_vm_start(); // 检查是否设置了EagerXrunInit标志,如果设置了,则会尽早 // 启动-Xrun代理。 if (EagerXrunInit) { // 如果设置了该标志,则调用JvmtiAgentList::load_xrun_agents() // 函数来加载代理 JvmtiAgentList::load_xrun_agents(); } // 初始化Java核心java.lang包中的类,通过传入的主线程和检查 // JNI错误的参数来实现。 // 第1阶段:java.lang.系统类初始化。 // java.lang.System是一个原始类,由VM在启动初期加载和初始化。 // java.lang.System.<clinit>只执行registerNatives, // 并在线程初始化完成之前保留类初始化的其余工作。 // System.initPhase1初始化系统属性、静态字段in、out和err。 // 设置java信号处理程序、操作系统特定的系统设置和主线程的线程组。 initialize_java_lang_classes(main_thread, CHECK_JNI_ERR); // 加速JNI函数,通过使用快速版本替换获取基础类型的函数 quicken_jni_functions(); // 在这个点,存根代码不允许再生成了 // StubCodeDesc描述一段生成的代码(通常是存根)。此信息主要 // 用于调试和打印。目前代码描述符只是简单地链接在一个链表中, // 如果搜索变得太慢,这种情况可能不得不改变。 StubCodeDesc::freeze(); // 设置了一个标志,表示基本初始化已经完成。这个标志被异常和 // 各种调试工具使用,在所有基本类都被初始化之前无法使用 set_init_completed(); // 下面三行代码分别调用了三个类的post_initialize()函数。 // 其中LogConfiguration类用于配置日志输出, // Metaspace类用于管理元空间, // MutexLocker类用于管理锁。 // 这些函数的作用是在程序启动后初始化相关的资源和状态,以便 // 后续的程序运行。每个类的post_initialize()函数都会执行一些 // 初始化操作,例如设置默认值、分配内存等。这样可以确保程序在 // 正式运行前已经准备好了必要的资源和状态,从而提高程序的 // 稳定性和性能 LogConfiguration::post_initialize(); Metaspace::post_initialize(); MutexLocker::post_initialize(); // Java虚拟机初始化完成 HOTSPOT_VM_INIT_END(); // 如果包含管理功能,则调用Management类的 // record_vm_init_completed()方法记录虚拟机初始化完成的时间 #if INCLUDE_MANAGEMENT Management::record_vm_init_completed(); #endif // INCLUDE_MANAGEMENT // 打印初始化JVM的进程ID log_info(os)("Initialized VM with process ID %d", os::current_process_id()); // 在VMInit事件被发布之前启动Signal Dispatcher os::initialize_jdk_signal_support(CHECK_JNI_ERR); // AttachListener线程通过客户端工具为排队的操作队列提供服务。 // 每个操作都由一个名称标识,最多有3个参数。 // 操作名称映射到执行操作的函数,被outputStream调用, // 该输出流可用于写入任何结果数据(例如,序列化写入properties // 名称和值)当该函数执行完成,会将所有结果数据返回给客户端工具 if (!DisableAttachMechanism) { // 如果没有禁用Attach 机制,则启动AttachListener。 AttachListener::vm_start(); if (StartAttachListener || AttachListener::init_at_startup()) { AttachListener::init(); } } // 如果没有设置EagerXrunInit,则启动-Xrun代理 if (!EagerXrunInit) { JvmtiAgentList::load_xrun_agents(); } // 启动一个清理内存池的任务 Chunk::start_chunk_pool_cleaner_task(); // JVMTI是Java虚拟机工具接口(Java Virtual Machine Tool Interface) // 的缩写。它是一组用于Java虚拟机的原生编程接口,允许开发人员 // 创建各种工具来监视、管理和分析Java应用程序的行为。JVMTI提供了 // 对Java虚拟机内部状态的访问,包括类、对象、线程和堆信息等。 // 它还允许开发人员创建自己的代理程序,以便在Java应用程序中动态 // 植入代码。JVMTI常用于Java应用程序的性能分析、调试和测试等方面。 // 启动了一个服务线程,该服务线程将JVMTI延迟事件排队,并进行 // 各种哈希表和其他清理工作。需要在编译器开始发布事件之前启动。 ServiceThread::initialize(); // 启动了监视器缩减线程。首先,代码调用了MonitorDeflationThread // 类的initialize()方法。这个方法可能会执行一些初始化操作,然后 // 启动一个后台线程,该线程会定期检查系统中的监视器,以确保它们 // 不会占用太多的内存。如果监视器过大,线程会自动将其缩小。 // 这个线程将在后台运行,直到系统关闭或线程被显式停止。 MonitorDeflationThread::initialize(); // 以下宏条件中的代码,意思是初始化编译器 // JVMCI是Java虚拟机编译接口(JVM Compiler Interface)的缩写, // 它是Java 9引入的一个新的JIT编译器接口。JVMCI允许第三方开发者 // 编写JIT编译器,并将其集成到Java虚拟机中,以替代默认的JIT编译 // 器。JVMCI的引入使得Java虚拟机更加灵活,可以根据具体的应用 // 场景选择更加适合的JIT编译器,从而提高Java应用程序的性能。 #if defined(COMPILER1) || COMPILER2_OR_JVMCI #if INCLUDE_JVMCI bool force_JVMCI_intialization = false; if (EnableJVMCI) { // 在初始化JVMCI时,如果启用了EagerJVMCI或者 // JVMCIPrintProperties或者JVMCILibDumpJNIConfig, // 则强制初始化JVMCI。否则,如果使用了JVMCI编译器 // 并且不使用解释器或后台编译,则强制初始化JVMCI force_JVMCI_intialization = EagerJVMCI || JVMCIPrintProperties || JVMCILibDumpJNIConfig; if (!force_JVMCI_intialization) { force_JVMCI_intialization = UseJVMCICompiler && (!UseInterpreter || !BackgroundCompilation); } } #endif CompileBroker::compilation_init_phase1(CHECK_JNI_ERR); if (JVMCI_ONLY(!force_JVMCI_intialization) NOT_JVMCI(true)) { CompileBroker::compilation_init_phase2(); } #endif // 如果启用了字符串去重功能,则启动字符串去重线程 if (StringDedup::is_enabled()) { StringDedup::start(); } // 预初始化一些JSR292核心类,以避免在类加载期间发生死锁。 // 它在编译器初始化后执行,因为否则可能会错过签名多态 // MH内部函数的编译。方法内部代码步骤: // 1. 初始化java.lang包中类的方法处理的调用。 // 2. 初始化java.lang包中类的解析方法名称的调用。 // 3. 初始化java.lang包中类的成员名称的调用。 // 4. 初始化java.lang包中类的本地方法处理的调用。 initialize_jsr292_core_classes(CHECK_JNI_ERR); // 第2阶段初始化:初始化模块系统,并且只有在第二阶段完成之前 // 可以加载java.base类。在编译器初始化和jsr292类初始化 // System.initPhase2,因为模块初始化运行了大量java代码,出于 // 性能原因,应该编译这些代码。此外,这将使启动代码能够在此 // 阶段及以后使用lambda和其他语言功能。在第2阶段之后, // 虚拟机将从-Xbootclasspath/a开始搜索类。 call_initPhase2(CHECK_JNI_ERR); // 只有在启用了JFR(Java Flight Recorder)时才会执行该函数。 // JFR是一种用于收集和分析Java应用程序性能数据的工具 JFR_ONLY(Jfr::on_create_vm_2();) // 即使没有JVMTI环境也要调用,因为环境可能会在后期附加, // JVMTI必须跟踪VM执行阶段。 JvmtiExport::enter_start_phase(); // 通知JVMTI代理VM已经启动(JNI已经启动), // 如果没有代理则不执行任何操作。 JvmtiExport::post_vm_start(); // 第3阶段初始化 最后设置安全管理器、系统类加载器和TCCL。这将 // 实例化和设置安全管理器,设置系统类加载器以及线程上下文类 // 加载器。安全管理器和系统类加载器可以是从-Xbootclasspath/a、 // 其他模块或应用程序的类路径加载的自定义类。 call_initPhase3(CHECK_JNI_ERR); // 这段代码的作用是缓存系统和平台类加载器。在该方法中,首先, // 通过调用ClassLoader类的getSystemClassLoader方法和 // getPlatformClassLoader方法获取系统类加载器和平台类加载器, // 并将其缓存起来 SystemDictionary::compute_java_loaders(CHECK_JNI_ERR); // 如果定义了INCLUDE_CDS宏,则初始化类加载器的模块路径信息。 // 如果在初始化过程中出现异常,则会打印异常信息并退出虚拟机。 #if INCLUDE_CDS ClassLoader::initialize_module_path(THREAD); if (HAS_PENDING_EXCEPTION) { java_lang_Throwable::print(PENDING_EXCEPTION, tty); vm_exit_during_initialization("ClassLoader::initialize_module_path() failed unexpectedly"); } #endif // 在JVMCI被包含的情况下,如果需要强制初始化JVMCI, // 则进行编译器的初始化,并执行编译初始化的第二阶段 #if INCLUDE_JVMCI if (force_JVMCI_intialization) { JVMCI::initialize_compiler(CHECK_JNI_ERR); CompileBroker::compilation_init_phase2(); } #endif // 方法是JVMTI API的一部分,用于通知JVMTI环境进入VM的“live”阶段, // 即VM正在运行并且可以被监视和分析。这个方法应该在VM启动后尽早 // 调用,因为JVMTI需要跟踪VM执行的不同阶段。如果JVMTI环境在后期 // 附加,这个方法也应该被调用,以确保JVMTI能够正确跟踪VM的状态 JvmtiExport::enter_live_phase(); // 使性能内存可访问 // 性能内存通常用于存储程序运行时的性能数据,如CPU使用率、 // 内存使用率等。通过使性能内存可访问,开发人员可以获取这些数据 // 并进行分析,以便优化程序的性能。 PerfMemory::set_accessible(true); // 通知JVMTI代理VM初始化完成,如果没有代理则不执行任何操作 JvmtiExport::post_vm_initialized(); // 在启用JFR时,当虚拟机被创建时记录其配置信息。 JFR_ONLY(Jfr::on_create_vm_3();) // 如果定义了INCLUDE_MANAGEMENT宏,则初始化类管理器, // 如果有异常,则退出VM。 #if INCLUDE_MANAGEMENT Management::initialize(THREAD); if (HAS_PENDING_EXCEPTION) { vm_exit(1); } #endif // 调用统计采样器(StatSampler) StatSampler::engage(); if (CheckJNICalls) JniPeriodicChecker::engage(); // 如果定义了INCLUDE_RTM_OPT宏,则初始化用于计数的锁,它允许 // 多个线程同时访问计数器,并确保计数器的值正确地增加或减少。 #if INCLUDE_RTM_OPT RTMLockingCounters::init(); #endif // 调用VM初始化完成后的钩子方法,Java库方法本身可以独立于VM进行更改 call_postVMInitHook(THREAD); // 在PostVMInitHook.run方法的Java部分处理所有异常并提供诊断手段。 // 如果存在挂起异常,则清除挂起异常。. if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; } // 启动一个WatcherThread线程来执行周期性任务 { // 通过MutexLocker来锁定PeriodicTask_lock,确保WatcherThread // 可以通过WatcherThread::start()或动态注册来启动 MutexLocker ml(PeriodicTask_lock); // 将WatcherThread设置为可启动状态 WatcherThread::make_startable(); // 如果有周期性任务,则启动WatcherThread线程来执行任务。 // 需要注意的是,所有的周期性任务应该在此之前都已经注册了, // 否则晚注册的任务可能会启动缓慢。 if (PeriodicTask::num_tasks() > 0) { WatcherThread::start(); } } // 结束创建虚拟机的计时器计时 create_vm_timer.end(); #ifdef ASSERT _vm_complete = true; #endif // 判断是否启用了DumpSharedSpaces选项,如果启用, // 则调用MetaspaceShared::preload_and_dump()函数, // 该函数的作用是预加载和转储共享空间。然后,代码调用 // ShouldNotReachHere()函数,该函数的作用是抛出一个错误, // 表示代码执行到了不应该到达的地方。 if (DumpSharedSpaces) { MetaspaceShared::preload_and_dump(); ShouldNotReachHere(); } return JNI_OK; }
下面用一张流程图概括: