HotSpot初始化

openjdk9 JVM启动调试介绍了从Launcher到主线程的整个初始化运行过程。这一篇集中介绍主线程中InitializeJVM最终调用Threads::create_vm初始化hotspot虚拟机各个模块的过程。

1.hotspot各模块

    └── vm              HotSpot VM的核心代码
        ├── adlc          平台描述文件(上面的cpu或os_cpu里的*.ad文件)的编译器
        ├── asm           汇编器接口
        ├── c1            client编译器(又称“C1”)
        ├── ci            动态编译器的公共服务/从动态编译器到VM的接口
        ├── classfile     类文件的处理(包括类加载和系统符号表等)
        ├── code          动态生成的代码的管理
        ├── compiler      从VM调用动态编译器的接口
        ├── gc_implementation    GC的实现
        │   ├── concurrentMarkSweep 
        │   ├── g1
        │   ├── parallelScavenge
        │   ├── parNew
        │   └── shared      GC的通用实现
        ├── gc_interface    GC接口
        ├── interpreter     解释器,包括“模板解释器”(官方版在用)和“C++解释器”(官方版不在用)
        ├── libadt          一些抽象数据结构
        ├── memory          内存管理相关(老的分代式GC框架也在这里)
        ├── oops            HotSpot VM的对象系统的实现
        ├── opto            server编译器(又称“C2”或“Opto”)
        ├── precompiled
        ├── prims           HotSpot VM的对外接口,包括部分标准库的native部分和JVMTI实现
        ├── runtime         运行时支持库(包括线程管理、编译器调度、锁、反射等)
        ├── services        主要是用来支持JMX之类的管理功能的接口
        ├── shark           基于LLVM的JIT编译器(官方版里没有使用)
        ├── trace
        └── utilities       一些基本的工具类

hotspot所处的位置,参考Java Platform Standard Edition 8 Documentation

1.1 Prims模块

主要负责提供外部程序访问JVM内部信息的对外接口,包括JNI、JVM、JVMTI和Perf。


1.1.1 JNI子模块

JNI——Java Native Interface。

为什么需要JNI?

Java源码被编译为字节码,是一种符合JVM规范能够被识别及运行的高度优化指令。屏蔽了与底层操作系统和物理硬件运行的一些特性。如果希望去访问底层操作系统和物理硬件资源,就需要JNI技术让Java代码和使用其他编程语言代码进行交互。hotspot中以"jni_"开头的函数都是由C/C++编写的本地代码,用于让Java调用JVM中的本地库函数。


jni.h示例

struct JNIEnv_ {
    const struct JNINativeInterface_ *functions;

    jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
                       jsize len) {
        return functions->DefineClass(this, name, loader, buf, len);
    }
    jclass FindClass(const char *name) {
        return functions->FindClass(this, name);
    }

    jobject CallObjectMethodV(jobject obj, jmethodID methodID,
                        va_list args) {
        return functions->CallObjectMethodV(this,obj,methodID,args);
    }

    jint CallIntMethod(jobject obj, jmethodID methodID, ...) {
        va_list args;
        jint result;
        va_start(args,methodID);
        result = functions->CallIntMethodV(this,obj,methodID,args);
        va_end(args);
        return result;
    }
};

struct JavaVM_ {
    const struct JNIInvokeInterface_ *functions;

    jint DestroyJavaVM() {
        return functions->DestroyJavaVM(this);
    }
};

1.1.2 JVM子模块

JVM模块是在JNI模块进行了扩充,JVM模块所包含的本地方法全都是与JVM相关的底层函数,以“JVM_”开头的函数全都是由本地代码实现的Java API。

大部分Java API都需要调用本地代码去实现,如:java.lang.Object java.lang.String java.lang.System Thread IO等。

jvm.h示例:

/*************************************************************************
 PART 1: Functions for Native Libraries
 ************************************************************************/
/*
 * java.lang.Object
 */
JNIEXPORT jint JNICALL
JVM_IHashCode(JNIEnv *env, jobject obj);

JNIEXPORT void JNICALL
JVM_MonitorWait(JNIEnv *env, jobject obj, jlong ms);

/*
 * java.lang.String
 */
JNIEXPORT jstring JNICALL
JVM_InternString(JNIEnv *env, jstring str);

/*
 * java.lang.Thread
 */
JNIEXPORT void JNICALL
JVM_StartThread(JNIEnv *env, jobject thread);

/*
 * java.lang.Class and java.lang.ClassLoader
 */
/*
 * Returns the class in which the code invoking the native method
 * belongs.
 *
 * Note that in JDK 1.1, native methods did not create a frame.
 * In 1.2, they do. Therefore native methods like Class.forName
 * can no longer look at the current frame for the caller class.
 */
JNIEXPORT jclass JNICALL
JVM_GetCallerClass(JNIEnv *env, int n);
/*************************************************************************
 PART 2: Support for the Verifier and Class File Format Checker
 ************************************************************************/
JNIEXPORT const char * JNICALL
JVM_GetClassNameUTF(JNIEnv *env, jclass cb);
/*************************************************************************
 PART 3: I/O and Network Support
 ************************************************************************/
JNIEXPORT int
jio_fprintf(FILE *, const char *fmt, ...);

1.1.3 JVMTI子模块

JVMTI——Java Virtual Machine Tool Interface,JVM工具接口。用于取代JVMPI(Profiling Interface剖析接口)和(Debugging Interface调试接口)。可以通过JVMTI创建代理程序Agent对Java程序进行监视和控制,包括内存使用率、CPU使用率和线程分析等。



Agent可以向运行中的JVM订阅感兴趣的事件,同时JVMTI提供了查询和控制Java程序运行状态的函数。

1.1.4 Perf子模块

是jdk.internal.perf.Perf底层实现,函数名以“PERF_”开头,用于监控JVM内部的Perf Data计数器。

1.2 Runtime模块

包含如下子模块:

  • Thread子模块
  • Arguments子模块,标记和传递JVM参数选项
  • StubRoutines、StubCodeGenerator子模块
  • Frame子模块,表示物理栈帧
  • CompilationPolicy,匹配编译策略
  • Init,os初始化
  • VMThread,创建其他线程
  • VMOperation,虚拟机核心操作

1.2.1 Thead子模块

定义了两种线程类型,分别是守护线程和非守护线程。

该模块还包含一个Threads子模块用于维护JVM中的线程队列,并由Threads模块中的create_vm()函数最终完成堆JVM的初始化。

2.Threads::create_vm

jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
  // 检查版本
  extern void JDK_Version_init();

  // Preinitialize version info.
  VM_Version::early_initialize();

  // Check version
  if (!is_supported_jni_version(args->version)) return JNI_EVERSION;

  // Initialize library-based TLS
  // 初始化TLS
  ThreadLocalStorage::init();

  // Initialize the output stream module
  // 初始化输出流模块
  ostream_init();

  // Process java launcher properties.
  // 配置Laucher的一些相关参数
  Arguments::process_sun_java_launcher_properties(args);

  // Initialize the os module
  // 初始化os模块
  os::init();

  // Record VM creation timing statistics
  // 记录JVM创建时间
  TraceVmCreationTime create_vm_timer;
  create_vm_timer.start();

  // Initialize system properties.
  // 初始化系统属性
  Arguments::init_system_properties();

  // So that JDK version can be used as a discriminator when parsing arguments
  JDK_Version_init();

  // Update/Initialize System properties after JDK version number is known
  Arguments::init_version_specific_system_properties();

  // Make sure to initialize log configuration *before* parsing arguments
  LogConfiguration::initialize(create_vm_timer.begin_time());

  // Parse arguments
  // 参数解析,java命令的参数,例如-XX:+UseParallelGC
  jint parse_result = Arguments::parse(args);
  if (parse_result != JNI_OK) return parse_result;

  os::init_before_ergo();

  jint ergo_result = Arguments::apply_ergo();
  if (ergo_result != JNI_OK) return ergo_result;

  // Final check of all ranges after ergonomics which may change values.
  if (!CommandLineFlagRangeList::check_ranges()) {
    return JNI_EINVAL;
  }

  // Final check of all 'AfterErgo' constraints after ergonomics which may change values.
  bool constraint_result = CommandLineFlagConstraintList::check_constraints(CommandLineFlagConstraint::AfterErgo);
  if (!constraint_result) {
    return JNI_EINVAL;
  }

  CommandLineFlagWriteableList::mark_startup();

  if (PauseAtStartup) {
    os::pause();
  }

  HOTSPOT_VM_INIT_BEGIN();

  // Timing (must come after argument parsing)
  TraceTime timer("Create VM", TRACETIME_LOG(Info, startuptime));

  // Initialize the os module after parsing the args
  // 解析参数后,对os模块(内存、栈、线程)进行初始化
  jint os_init_2_result = os::init_2();
  if (os_init_2_result != JNI_OK) return os_init_2_result;

  jint adjust_after_os_result = Arguments::adjust_after_os();
  if (adjust_after_os_result != JNI_OK) return adjust_after_os_result;

  // Initialize output stream logging
  ostream_init_log();

  // Convert -Xrun to -agentlib: if there is no JVM_OnLoad
  // Must be before create_vm_init_agents()
  if (Arguments::init_libraries_at_startup()) {
    convert_vm_init_libraries_to_agents();
  }

  // Launch -agentlib/-agentpath and converted -Xrun agents
  if (Arguments::init_agents_at_startup()) {
    create_vm_init_agents();
  }

  // Initialize Threads state
  _thread_list = NULL;
  _number_of_threads = 0;
  _number_of_non_daemon_threads = 0;

  // Initialize global data structures and create system classes in heap
  // 初始化全局数据结构,并在堆上创建系统类
  vm_init_globals();

#if INCLUDE_JVMCI
  if (JVMCICounterSize > 0) {
    JavaThread::_jvmci_old_thread_counters = NEW_C_HEAP_ARRAY(jlong, JVMCICounterSize, mtInternal);
    memset(JavaThread::_jvmci_old_thread_counters, 0, sizeof(jlong) * JVMCICounterSize);
  } else {
    JavaThread::_jvmci_old_thread_counters = NULL;
  }
#endif // INCLUDE_JVMCI

  // Attach the main thread to this os thread
  JavaThread* main_thread = new JavaThread();
  main_thread->set_thread_state(_thread_in_vm);
  main_thread->initialize_thread_current();
  // must do this before set_active_handles
  main_thread->record_stack_base_and_size();
  main_thread->set_active_handles(JNIHandleBlock::allocate_block());

  if (!main_thread->set_as_starting_thread()) {
    vm_shutdown_during_initialization(
                                      "Failed necessary internal allocation. Out of swap space");
    delete main_thread;
    *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
    return JNI_ENOMEM;
  }

  // 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();

  // Initialize Java-Level synchronization subsystem
  // 初始化Java语言的同步子系统
  ObjectMonitor::Initialize();

  // Initialize global modules
  // 初始化全局模块
  jint status = init_globals();
  if (status != JNI_OK) {
    delete main_thread;
    *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
    return status;
  }

  if (TRACE_INITIALIZE() != JNI_OK) {
    vm_exit_during_initialization("Failed to initialize tracing backend");
  }

  // Should be done after the heap is fully created
  main_thread->cache_global_variables();

  HandleMark hm;

  { MutexLocker mu(Threads_lock);
    Threads::add(main_thread);
  }

  // Any JVMTI raw monitors entered in onload will transition into
  // real raw monitor. VM is setup enough here for raw monitor enter.
  JvmtiExport::transition_pending_onload_raw_monitors();

  // Create the VMThread
  { TraceTime timer("Start VMThread", TRACETIME_LOG(Info, startuptime));

  VMThread::create();
    Thread* vmthread = VMThread::vm_thread();

    if (!os::create_thread(vmthread, os::vm_thread)) {
      vm_exit_during_initialization("Cannot create VM thread. "
                                    "Out of system resources.");
    }

    // Wait for the VM thread to become ready, and VMThread::run to initialize
    // Monitors can have spurious returns, must always check another state flag
    {
      MutexLocker ml(Notify_lock);
      os::start_thread(vmthread);
      while (vmthread->active_handles() == NULL) {
        Notify_lock->wait();
      }
    }
  }

  assert(Universe::is_fully_initialized(), "not initialized");
  if (VerifyDuringStartup) {
    // Make sure we're starting with a clean slate.
    VM_Verify verify_op;
    VMThread::execute(&verify_op);
  }

  Thread* THREAD = Thread::current();

  // At this point, the Universe is initialized, but we have not executed
  // any byte code.  Now is a good time (the only time) to dump out the
  // internal state of the JVM for sharing.
  if (DumpSharedSpaces) {
    MetaspaceShared::preload_and_dump(CHECK_JNI_ERR);
    ShouldNotReachHere();
  }

  // Always call even when there are not JVMTI environments yet, since environments
  // may be attached late and JVMTI must track phases of VM execution
  JvmtiExport::enter_early_start_phase();

  // Notify JVMTI agents that VM has started (JNI is up) - nop if no agents.
  JvmtiExport::post_early_vm_start();

  // 初始化Java的一些基础类库
  initialize_java_lang_classes(main_thread, CHECK_JNI_ERR);

  // We need this for ClassDataSharing - the initial vm.info property is set
  // with the default value of CDS "sharing" which may be reset through
  // command line options.
  reset_vm_info_property(CHECK_JNI_ERR);

  quicken_jni_functions();

  // No more stub generation allowed after that point.
  StubCodeDesc::freeze();

  // Set flag that basic initialization has completed. Used by exceptions and various
  // debug stuff, that does not work until all basic classes have been initialized.
  set_init_completed();

  LogConfiguration::post_initialize();
  Metaspace::post_initialize();

  HOTSPOT_VM_INIT_END();

  // record VM initialization completion time
#if INCLUDE_MANAGEMENT
  // 初始化JVM初始化完成时间
  Management::record_vm_init_completed();
#endif // INCLUDE_MANAGEMENT

  // Signal Dispatcher needs to be started before VMInit event is posted
  // 初始化os模块的信号系统,这样JVM才可以向os发送相应信号信息
  os::signal_init();

  // Start Attach Listener if +StartAttachListener or it can't be started lazily
  // 启动Attach Listener线程
  if (!DisableAttachMechanism) {
    AttachListener::vm_start();
    if (StartAttachListener || AttachListener::init_at_startup()) {
      AttachListener::init();
    }
  }

  // Launch -Xrun agents
  // Must be done in the JVMTI live phase so that for backward compatibility the JDWP
  // back-end can launch with -Xdebug -Xrunjdwp.
  if (!EagerXrunInit && Arguments::init_libraries_at_startup()) {
    create_vm_init_libraries();
  }

  if (CleanChunkPoolAsync) {
    Chunk::start_chunk_pool_cleaner_task();
  }

  // initialize compiler(s)
  // 初始化即时编译器
#if defined(COMPILER1) || defined(COMPILER2) || defined(SHARK) || INCLUDE_JVMCI
  CompileBroker::compilation_init(CHECK_JNI_ERR);
#endif

  // Pre-initialize some JSR292 core classes to avoid deadlock during class loading.
  // It is done after compilers are initialized, because otherwise compilations of
  // signature polymorphic MH intrinsics can be missed
  // (see SystemDictionary::find_method_handle_intrinsic).
  initialize_jsr292_core_classes(CHECK_JNI_ERR);

  // This will initialize the module system.  Only java.base classes can be
  // loaded until phase 2 completes
  call_initPhase2(CHECK_JNI_ERR);

  // Always call even when there are not JVMTI environments yet, since environments
  // may be attached late and JVMTI must track phases of VM execution
  JvmtiExport::enter_start_phase();

  // Notify JVMTI agents that VM has started (JNI is up) - nop if no agents.
  JvmtiExport::post_vm_start();

  // Final system initialization including security manager and system class loader
  call_initPhase3(CHECK_JNI_ERR);

  // cache the system class loader
  // 指定类加载器
  SystemDictionary::compute_java_system_loader(CHECK_(JNI_ERR));

#if INCLUDE_JVMCI
  if (EnableJVMCI) {
    // Initialize JVMCI eagerly if JVMCIPrintProperties is enabled.
    // The JVMCI Java initialization code will read this flag and
    // do the printing if it's set.
    bool init = JVMCIPrintProperties;

    if (!init) {
      // 8145270: Force initialization of JVMCI runtime otherwise requests for blocking
      // compilations via JVMCI will not actually block until JVMCI is initialized.
      init = UseJVMCICompiler && (!UseInterpreter || !BackgroundCompilation);
    }

    if (init) {
      JVMCIRuntime::force_initialization(CHECK_JNI_ERR);
    }
  }
#endif

  // Always call even when there are not JVMTI environments yet, since environments
  // may be attached late and JVMTI must track phases of VM execution
  JvmtiExport::enter_live_phase();

  // Notify JVMTI agents that VM initialization is complete - nop if no agents.
  JvmtiExport::post_vm_initialized();

  if (TRACE_START() != JNI_OK) {
    vm_exit_during_initialization("Failed to start tracing backend.");
  }

#if INCLUDE_MANAGEMENT
  // 初始化Management模块
  Management::initialize(THREAD);

  if (HAS_PENDING_EXCEPTION) {
    // management agent fails to start possibly due to
    // configuration problem and is responsible for printing
    // stack trace if appropriate. Simply exit VM.
    vm_exit(1);
  }
#endif // INCLUDE_MANAGEMENT

  if (Arguments::has_profile())       FlatProfiler::engage(main_thread, true);
  if (MemProfiling)                   MemProfiler::engage();
  StatSampler::engage();
  if (CheckJNICalls)                  JniPeriodicChecker::engage();

  BiasedLocking::init();

#if INCLUDE_RTM_OPT
  RTMLockingCounters::init();
#endif

  if (JDK_Version::current().post_vm_init_hook_enabled()) {
    call_postVMInitHook(THREAD);
    // The Java side of PostVMInitHook.run must deal with all
    // exceptions and provide means of diagnosis.
    if (HAS_PENDING_EXCEPTION) {
      CLEAR_PENDING_EXCEPTION;
    }
  }

  {
    MutexLocker ml(PeriodicTask_lock);
    // Make sure the WatcherThread can be started by WatcherThread::start()
    // or by dynamic enrollment.
    WatcherThread::make_startable();
    // Start up the WatcherThread if there are any periodic tasks
    // NOTE:  All PeriodicTasks should be registered by now. If they
    //   aren't, late joiners might appear to start slowly (we might
    //   take a while to process their first tick).
    if (PeriodicTask::num_tasks() > 0) {
      // 启动WacherThread线程,用以支持定时器等周期性任务
      WatcherThread::start();
    }
  }

  create_vm_timer.end();
#ifdef ASSERT
  _vm_complete = true;
#endif
  return JNI_OK;
}
  • JDK_Version_init()首先检测一下系统支不支持这个版本的JVM,检查的是平台和JVM的兼容性;
  • is_supported_jni_version检查是否支持当前JNI版本
  • ThreadLocalStorage::init()初始化一下TLS(Thread-Local-Storage);
  • ostream_init()初始化输出流模块
  • Arguments::process_sun_java_launcher_properties配置Laucher的一些相关参数
  • os::init()初始化os模块,包括随机数生成器、当前进程id、高精度计时器、内存页尺寸以及保护页
  • Arguments::init_system_properties()初始化系统属性,比如java.home,java.class.path,java.vm.specification.version
  • JDK_Version_init()初始化JDK版本,这个东西将影响后面的系统参数。我举例来说,不同的JDK版本的默认GC是不同的。所以JDK版本要先初始化,后面加载系统参数的时候才好决定默认值是什么;
  • LogConfiguration::initialize初始化一下log。debug用的;
  • Arguments::parse参数解析,java命令的参数,例如-XX:+UseParallelGC
  • Arguments::apply_ergo()初始化Ergonomics的东西。Ergonomics和GC息息相关,在garbage collection tuning guide里面有专门的一章讲这个东西。这个东西大概就是影响GC、堆的默认设置,还有根据设置的最大停顿时间以及吞吐量目标,搞点自适应的东西;
  • os::init_2()解析参数后,再一次初始化os模块,为什么要分两次来初始化。在这个步骤之前的初始化,是依赖于os模块的,比如说Ergonomics那个部分就会涉及到内存页大小的东西,所以它要先初始化一下;但是os模块还和一些系统参数有关,系统参数又依赖于os模块。所以JVM的设计者索性分成两次来初始化,先初始化一波独立的无依赖的os模块部分,等前面一大波都初始化之后,再次初始化一下。这一次初始化之后os模块就和jni版本,jdk版本很契合了;
  • convert_vm_init_libraries_to_agents(),create_vm_init_agents()处理-agentlib/-agentpath,-Xrun agents
  • 初始化线程相关变量_thread_list等等;
  • vm_init_globals()初始化全局数据结构,并在堆上创建系统类。
    check_ThreadShadow();
    basic_types_init();
    eventlog_init(); 事件日志
    mutex_init(); 锁机制
    chunkpool_init(); 内存分配器
    perfMemory_init(); 性能统计数据内存
    SuspendibleThreadSet_init();
  • 处理main thread。包括:将main thread附着到当前的系统线程上。

就到现在为止,我们讨论的东西都还是系统层面上的东西,还没到Java应用层面上的东西。这个部分就是将两者结合起来,一个非常关键的点。

  • ObjectMonitor::Initialize()初始化JVM的同步机制,也就是Java的Object Monitor机制。Java的synchronize关键字的实现,关键初始化就发生在这一步。
  • init_globals()初始化全局模块。方法做的部分事情:字节码初始化、类加载器初始化、JIT编译策略初始化、代码缓存初始化、解释器初始化...可以说,和Java代码执行的大部分事情,基本上都在这里初始化。
    management_init();
    bytecodes_init();
    classLoader_init1();
    compilationPolicy_init();
    codeCache_init();
    VM_Version_init();
    os_init_globals();
    stubRoutines_init1();
    jint status = universe_init();
    interpreter_init();
    invocationCounter_init();
    marksweep_init();
    accessFlags_init();
    templateTable_init();
    InterfaceSupport_init();
    SharedRuntime::generate_stubs();
    universe2_init();
    referenceProcessor_init();
    jni_handles_init();
    vtableStubs_init();
    InlineCacheBuffer_init();
    compilerOracle_init();
    dependencyContext_init();
    javaClasses_init();
    stubRoutines_init2();
    MethodHandles::generate_adapters();
  • VMThread::create()创建VMThread并且等其就绪。JVM线程大概就分两类,一类是为了维系JVM自身的线程,如GC线程什么的;另一类就是我们在Java代码里面显式创建的线程。这部分就是前一类线程初始化的地方;
  • JvmtiExport::enter_early_start_phase()和JvmtiExport::post_early_vm_start()处理JVMTI。
  • initialize_java_lang_classes初始化java lang class。主要就是加载了Java lang包下的一些类。比如OOM异常就是在这里加载的。
  • quicken_jni_functions()初始化Java native interface的东西,主要就是处理了一下类型问题;
  • Management::record_vm_init_completed()标记JVM初始化完成;
  • os::signal_init()初始化signal dispatcher,这样JVM才可以向os发送相应信号信息。这是将操作系统层面的signal和jdk结合起来的关键点;
  • AttachListener::vm_start()启动Attach Listener线程
  • CompileBroker::compilation_init,JIT编译器初始化,注意和init_globals()里面的JIT编译策略初始化相区别,并启动hotspot编译代理线程
  • initialize_jsr292_core_classes预先加载一些JSR292里的类,为了避免死锁;
  • call_initPhase2初始化模块系统,这是JDK9加入的特性;
  • JVMTI生命周期的通知,如enter_start_phase,post_vm_start等;
  • call_initPhase3系统初始化的最后一部分,包括system class loader, security manager;
  • SystemDictionary::compute_java_system_loader缓存系统类加载器
  • Management::initialize(THREAD)初始化Management模块
  • 如果设置了一些诸如内存分析(mem profile)之类的东西,那么就初始化。这个部分可以理解为初始化一些和JVM profile相关的东西;
  • BiasedLocking::init()。这个重要性不言而喻,偏向锁可以说是Java锁机制里面最重要的东西;
  • WatcherThread::start()启动WacherThread线程,用以支持定时器等周期性任务
  • 创建结束。

2.1 详细看下os两次初始化

位于hotspot/src/os/linux/vm/os_linux.cpp中。

2.1.1 os::init是在大部分全局参数解析前调用

包括如下一些步骤:

  • 设置内存页大小
  • 初始化系统信息(处理器、物理内存等)
  • 初始化操作系统信息
  • 获取原生主线程的句柄
  • 初始化系统时钟
// this is called _before_ the most of global arguments have been parsed
void os::init(void) {
  char dummy;   // used to get a guess on initial stack address
//  first_hrtime = gethrtime();

  clock_tics_per_sec = sysconf(_SC_CLK_TCK);

  init_random(1234567);

  ThreadCritical::initialize();

  // 设置内存页大小
  Linux::set_page_size(sysconf(_SC_PAGESIZE));
  if (Linux::page_size() == -1) {
    fatal("os_linux.cpp: os::init: sysconf failed (%s)",
          os::strerror(errno));
  }
  init_page_sizes((size_t) Linux::page_size());

  // 初始化系统信息(处理器、物理内存等)
  Linux::initialize_system_info();

  Linux::initialize_os_info();

  // main_thread points to the aboriginal thread
  // 获取原生主线程的句柄
  Linux::_main_thread = pthread_self();

  // 初始化系统时钟
  Linux::clock_init();
  initial_time_count = javaTimeNanos();

  // pthread_condattr initialization for monotonic clock
  int status;
  pthread_condattr_t* _condattr = os::Linux::condAttr();
  if ((status = pthread_condattr_init(_condattr)) != 0) {
    fatal("pthread_condattr_init: %s", os::strerror(status));
  }
  // Only set the clock if CLOCK_MONOTONIC is available
  if (os::supports_monotonic_clock()) {
    if ((status = pthread_condattr_setclock(_condattr, CLOCK_MONOTONIC)) != 0) {
      if (status == EINVAL) {
        warning("Unable to use monotonic clock with relative timed-waits" \
                " - changes to the time-of-day clock may have adverse affects");
      } else {
        fatal("pthread_condattr_setclock: %s", os::strerror(status));
      }
    }
  }
  // else it defaults to CLOCK_REALTIME

  // retrieve entry point for pthread_setname_np
  Linux::_pthread_setname_np =
    (int(*)(pthread_t, const char*))dlsym(RTLD_DEFAULT, "pthread_setname_np");

}

2.1.2 os::init_2是在全局参数解析后调用

主要针对内存、栈、线程等与os模块密切相关的部分进行初始化。
包括如下一些步骤:

  • 分配共享内存,设置大页内存
  • 初始化内核信号,安装信号处理函数SR_handler
  • 对线程栈进行一系列配置,比如设置线程栈大小、分配线程初始栈
  • NUMA相关配置
  • 文件描述符数量相关配置
  • 初始化用于线程创建的锁
  • 初始化线程优先级策略
// this is called _after_ the global arguments have been parsed
jint os::init_2(void) {
  Linux::fast_thread_clock_init();

  // Allocate a single page and mark it as readable for safepoint polling
  // 分配共享内存,设置大页内存
  address polling_page = (address) ::mmap(NULL, Linux::page_size(), PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  guarantee(polling_page != MAP_FAILED, "os::init_2: failed to allocate polling page");

  os::set_polling_page(polling_page);
  log_info(os)("SafePoint Polling address: " INTPTR_FORMAT, p2i(polling_page));

  if (!UseMembar) {
    address mem_serialize_page = (address) ::mmap(NULL, Linux::page_size(), PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    guarantee(mem_serialize_page != MAP_FAILED, "mmap Failed for memory serialize page");
    os::set_memory_serialize_page(mem_serialize_page);
    log_info(os)("Memory Serialize Page address: " INTPTR_FORMAT, p2i(mem_serialize_page));
  }

  // initialize suspend/resume support - must do this before signal_sets_init()
  // 初始化内核信号,安装信号处理函数SR_handler
  if (SR_initialize() != 0) {
    perror("SR_initialize failed");
    return JNI_ERR;
  }

  Linux::signal_sets_init();
  Linux::install_signal_handlers();

  // Check and sets minimum stack sizes against command line options
  // 对线程栈进行一系列配置,比如设置线程栈大小、分配线程初始栈
  if (Posix::set_minimum_stack_sizes() == JNI_ERR) {
    return JNI_ERR;
  }
  Linux::capture_initial_stack(JavaThread::stack_size_at_create());

#if defined(IA32)
  workaround_expand_exec_shield_cs_limit();
#endif

  Linux::libpthread_init();
  log_info(os)("HotSpot is running with %s, %s",
               Linux::glibc_version(), Linux::libpthread_version());

  if (UseNUMA) {
    if (!Linux::libnuma_init()) {
      UseNUMA = false;
    } else {
      if ((Linux::numa_max_node() < 1)) {
        // There's only one node(they start from 0), disable NUMA.
        UseNUMA = false;
      }
    }
    // With SHM and HugeTLBFS large pages we cannot uncommit a page, so there's no way
    // we can make the adaptive lgrp chunk resizing work. If the user specified
    // both UseNUMA and UseLargePages (or UseSHM/UseHugeTLBFS) on the command line - warn and
    // disable adaptive resizing.
    if (UseNUMA && UseLargePages && !can_commit_large_page_memory()) {
      if (FLAG_IS_DEFAULT(UseNUMA)) {
        UseNUMA = false;
      } else {
        if (FLAG_IS_DEFAULT(UseLargePages) &&
            FLAG_IS_DEFAULT(UseSHM) &&
            FLAG_IS_DEFAULT(UseHugeTLBFS)) {
          UseLargePages = false;
        } else if (UseAdaptiveSizePolicy || UseAdaptiveNUMAChunkSizing) {
          warning("UseNUMA is not fully compatible with SHM/HugeTLBFS large pages, disabling adaptive resizing (-XX:-UseAdaptiveSizePolicy -XX:-UseAdaptiveNUMAChunkSizing)");
          UseAdaptiveSizePolicy = false;
          UseAdaptiveNUMAChunkSizing = false;
        }
      }
    }
    if (!UseNUMA && ForceNUMA) {
      UseNUMA = true;
    }
  }

  if (MaxFDLimit) {
    // set the number of file descriptors to max. print out error
    // if getrlimit/setrlimit fails but continue regardless.
    struct rlimit nbr_files;
    int status = getrlimit(RLIMIT_NOFILE, &nbr_files);
    if (status != 0) {
      log_info(os)("os::init_2 getrlimit failed: %s", os::strerror(errno));
    } else {
      nbr_files.rlim_cur = nbr_files.rlim_max;
      status = setrlimit(RLIMIT_NOFILE, &nbr_files);
      if (status != 0) {
        log_info(os)("os::init_2 setrlimit failed: %s", os::strerror(errno));
      }
    }
  }

  // Initialize lock used to serialize thread creation (see os::create_thread)
  // 初始化锁
  Linux::set_createThread_lock(new Mutex(Mutex::leaf, "createThread_lock", false));

  // at-exit methods are called in the reverse order of their registration.
  // atexit functions are called on return from main or as a result of a
  // call to exit(3C). There can be only 32 of these functions registered
  // and atexit() does not set errno.

  if (PerfAllowAtExitRegistration) {
    // only register atexit functions if PerfAllowAtExitRegistration is set.
    // atexit functions can be delayed until process exit time, which
    // can be problematic for embedded VM situations. Embedded VMs should
    // call DestroyJavaVM() to assure that VM resources are released.

    // note: perfMemory_exit_helper atexit function may be removed in
    // the future if the appropriate cleanup code can be added to the
    // VM_Exit VMOperation's doit method.
    if (atexit(perfMemory_exit_helper) != 0) {
      warning("os::init_2 atexit(perfMemory_exit_helper) failed");
    }
  }

  // initialize thread priority policy
  // 初始化线程优先级策略
  prio_init();

  return JNI_OK;
}

参考

  • 《Java虚拟机精讲》
  • Xcode debug Hotspot(二)——虚拟机创建过程

你可能感兴趣的:(HotSpot初始化)