概述
我们都知道JVM中有很多操作,都是要求线程达到安全点,例如典型的垃圾回收,本文将深入细节,了解JVM的具体实现;
JAVA代码一开始通常是采用解释执行,当执行一定次数,发现是属于热点代码时,会通过JIT编译为机器代码,提供执行效率;
JIT编译时会插入一些安全点检查的指令,会查询当前Java线程的polling_page,正常情况下,查询polling_page没有什么问题;但是当VM线程开始执行时,会将polling_page改为bad page;在linux系统中,访问不可读页面会产生SIGSEGV信号:
JIT插入安全点指令
//c1_LIRAssembler.cpp:LIR_Assembler::emit_op1->LIR_Assembler::safepoint_poll
int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) {
int offset = __ offset();
#ifdef _LP64
const Register poll_addr = rscratch1;
__ movptr(poll_addr, Address(r15_thread, JavaThread::polling_page_offset()));
#else
const Register poll_addr = tmp->as_register();
__ get_thread(poll_addr);
__ movptr(poll_addr, Address(poll_addr, in_bytes(JavaThread::polling_page_offset())));
#endif
add_debug_info_for_branch(info);
__ relocate(relocInfo::poll_type);
address pre_pc = __ pc();
__ testl(rax, Address(poll_addr, 0));
address post_pc = __ pc();
return offset;
}
关键代码是这里的 __ movptr(poll_addr, Address(r15_thread, JavaThread::polling_page_offset()))
polling_page
polling_page到底是什么东西呢,可以从JVM的启动代码里面看到其初始化流程
//Threads::create_vm->SafepointMechanism::initialize()->SafepointMechanism::default_initialize
const size_t page_size = os::vm_page_size();
const size_t allocation_size = 2 * page_size;
char* polling_page = os::reserve_memory(allocation_size);
os::commit_memory_or_exit(polling_page, allocation_size, false, "Unable to commit Safepoint polling page");
MemTracker::record_virtual_memory_type((address)polling_page, mtSafepoint);
char* bad_page = polling_page;
char* good_page = polling_page + page_size;
os::protect_memory(bad_page, page_size, os::MEM_PROT_NONE);
os::protect_memory(good_page, page_size, os::MEM_PROT_READ);
log_info(os)("SafePoint Polling address, bad (protected) page:" INTPTR_FORMAT ", good (unprotected) page:" INTPTR_FORMAT, p2i(bad_page), p2i(good_page));
// Poll address values
_poll_page_armed_value = reinterpret_cast<uintptr_t>(bad_page);
_poll_page_disarmed_value = reinterpret_cast<uintptr_t>(good_page);
_polling_page = (address)bad_page;
可以看到JVM启动时,申请了两个页,一个是可读的(good page),一个不可读(bad page);
VM线程
JVM中进入安全点的典型流程是通过VM线程,代码如下:
//vmThread.cpp:VMThread::inner_execute->SafepointSynchronize::begin->SafepointSynchronize::arm_safepoint
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {
// Make sure the threads start polling, it is time to yield.
SafepointMechanism::arm_local_poll(cur);
}
void SafepointMechanism::arm_local_poll(JavaThread* thread) {
thread->poll_data()->set_polling_word(_poll_word_armed_value);
thread->poll_data()->set_polling_page(_poll_page_armed_value);
}
进入安全点时,将将所有JAVA线程的_polling_page指向不可读页面(bad page);在linux系统中,访问一个MEM_PROT_NONE的页面会产生SIGSEGV;
而在JVM启动过程中,SIGSEGV信号已经被注册给了SafepointSynchronize::handle_polling_page_exception函数:
注册SIGSEGV信号
//os_linux_x86.cpp
bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
ucontext_t* uc, JavaThread* thread) {
......
if (sig == SIGSEGV && SafepointMechanism::is_poll_address((address)info->si_addr)) {
stub = SharedRuntime::get_poll_stub(pc);
}
......
}
address SharedRuntime::get_poll_stub(address pc) {
bool at_poll_return = ((CompiledMethod*)cb)->is_at_poll_return(pc);
bool has_wide_vectors = ((CompiledMethod*)cb)->has_wide_vectors();
if (at_poll_return) {
stub = SharedRuntime::polling_page_return_handler_blob()->entry_point();
} else if (has_wide_vectors) {
stub = SharedRuntime::polling_page_vectors_safepoint_handler_blob()->entry_point();
} else {
stub = SharedRuntime::polling_page_safepoint_handler_blob()->entry_point();
}
}
```c
void SharedRuntime::generate_stubs() {
......
_polling_page_safepoint_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_LOOP);
_polling_page_return_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_RETURN);
......
}
SIGSEGV信号处理
ThreadSafepointState::handle_polling_page_exception最终通过SafepointSynchronize::block暂停业务线程:
if (global_poll()) {
// Any load in ::block() must not pass the global poll load.
// Otherwise we might load an old safepoint counter (for example).
OrderAccess::loadload();
SafepointSynchronize::block(thread);
}
至此,安全点的触发机制都已经完全了解清楚;
网友评论