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(二)——虚拟机创建过程