深入剖析创建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;
}

  

下面用一张流程图概括:

  
posted @ 2023-06-13 23:08  编程老司机A  阅读(192)  评论(0编辑  收藏  举报