1. 信號處理與樁代碼(Stub)??
當線程訪問安全點輪詢頁(Polling Page)時:
- ??觸發 SIGSEGV 信號??:訪問只讀的輪詢頁會引發?
SIGSEGV
?異常。 - ??信號處理函數??:
pd_hotspot_signal_handler
?檢測到?SafepointMechanism::is_poll_address
?為真,調用?SharedRuntime::get_poll_stub
?獲取樁代碼入口地址(如?polling_page_safepoint_handler_blob
)。 - ??篡改 PC??:
os::Posix::ucontext_set_pc(uc, stub)
?將線程的 ??程序計數器(PC)?? 設置為樁代碼地址。
??2. 樁代碼的職責??
樁代碼(如?polling_page_safepoint_handler_blob
)是平臺相關的匯編代碼,其核心邏輯為:
asm
復制
// 偽代碼示例 call SafepointSynchronize::handle_polling_page_exception ; 調用安全點處理函數 ret
- ??直接調用??:樁代碼通過?
call
?指令直接調用?SafepointSynchronize::handle_polling_page_exception
。 - ??觸發阻塞??:
handle_polling_page_exception
?最終通過?SafepointSynchronize::block
?讓線程阻塞在安全點。
??3. 關鍵調用鏈??
信號處理與安全點處理的完整路徑:
信號處理函數 (javaSignalHandler)→ PosixSignals::pd_hotspot_signal_handler→ 檢測到安全點輪詢頁(SafepointMechanism::is_poll_address)→ SharedRuntime::get_poll_stub(pc) 獲取樁代碼地址→ 篡改 PC 到樁代碼(如 polling_page_safepoint_handler_blob)→ 樁代碼執行→ SafepointSynchronize::handle_polling_page_exception→ SafepointMechanism::process→ SafepointSynchronize::block→ 線程阻塞等待安全點
??4. 核心設計思想??
- ??信號驅動??:通過操作系統的內存保護機制(輪詢頁不可訪問)觸發信號,將控制權交給 JVM。
- ??間接跳轉??:信號處理函數不直接調用安全點邏輯,而是通過修改線程執行路徑(PC),跳轉到樁代碼。
- ??樁代碼橋接??:樁代碼作為 ??橋梁??,將信號處理上下文與 JVM 內部安全點處理邏輯連接。
??5. 普通線程阻塞的觸發??
- ??所有 Java 線程??:無論是用戶線程、JIT 編譯代碼線程,還是解釋器執行的線程,訪問輪詢頁時都會觸發此流程。
- ??統一入口??:無論線程原本在執行什么,最終都會通過樁代碼調用?
handle_polling_page_exception
,確保所有線程在安全點處阻塞。
??總結??
- ??信號處理函數不直接調用??:
handle_polling_page_exception
?由 ??樁代碼?? 直接調用,而非信號處理函數本身。 - ??間接觸發阻塞??:通過篡改 PC 到樁代碼,再由樁代碼觸發安全點處理邏輯,最終實現線程阻塞。
- ??統一安全點處理??:所有 Java 線程通過此機制在安全點同步,確保 GC 等操作的安全執行。
##源碼
address SharedRuntime::get_poll_stub(address pc) {address stub;// Look up the code blobCodeBlob *cb = CodeCache::find_blob(pc);// Should be an nmethodguarantee(cb != NULL && cb->is_compiled(), "safepoint polling: pc must refer to an nmethod");// Look up the relocation informationassert(((CompiledMethod*)cb)->is_at_poll_or_poll_return(pc),"safepoint polling: type must be poll");#ifdef ASSERTif (!((NativeInstruction*)pc)->is_safepoint_poll()) {tty->print_cr("bad pc: " PTR_FORMAT, p2i(pc));Disassembler::decode(cb);fatal("Only polling locations are used for safepoint");}
#endifbool at_poll_return = ((CompiledMethod*)cb)->is_at_poll_return(pc);bool has_wide_vectors = ((CompiledMethod*)cb)->has_wide_vectors();if (at_poll_return) {assert(SharedRuntime::polling_page_return_handler_blob() != NULL,"polling page return stub not created yet");stub = SharedRuntime::polling_page_return_handler_blob()->entry_point();} else if (has_wide_vectors) {assert(SharedRuntime::polling_page_vectors_safepoint_handler_blob() != NULL,"polling page vectors safepoint stub not created yet");stub = SharedRuntime::polling_page_vectors_safepoint_handler_blob()->entry_point();} else {assert(SharedRuntime::polling_page_safepoint_handler_blob() != NULL,"polling page safepoint stub not created yet");stub = SharedRuntime::polling_page_safepoint_handler_blob()->entry_point();}log_debug(safepoint)("... found polling page %s exception at pc = "INTPTR_FORMAT ", stub =" INTPTR_FORMAT,at_poll_return ? "return" : "loop",(intptr_t)pc, (intptr_t)stub);return stub;
}bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,ucontext_t* uc, JavaThread* thread) {if (sig == SIGILL &&((info->si_addr == (caddr_t)check_simd_fault_instr)|| info->si_addr == (caddr_t)check_vfp_fault_instr|| info->si_addr == (caddr_t)check_vfp3_32_fault_instr|| info->si_addr == (caddr_t)check_mp_ext_fault_instr)) {// skip faulty instruction + instruction that sets return value to// success and set return value to failure.os::Posix::ucontext_set_pc(uc, (address)info->si_addr + 8);uc->uc_mcontext.arm_r0 = 0;return true;}address stub = NULL;address pc = NULL;bool unsafe_access = false;if (info != NULL && uc != NULL && thread != NULL) {pc = (address) os::Posix::ucontext_get_pc(uc);// Handle ALL stack overflow variations hereif (sig == SIGSEGV) {address addr = (address) info->si_addr;// check if fault address is within thread stackif (thread->is_in_full_stack(addr)) {// stack overflowStackOverflow* overflow_state = thread->stack_overflow_state();if (overflow_state->in_stack_yellow_reserved_zone(addr)) {overflow_state->disable_stack_yellow_reserved_zone();if (thread->thread_state() == _thread_in_Java) {// Throw a stack overflow exception. Guard pages will be reenabled// while unwinding the stack.stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);} else {// Thread was in the vm or native code. Return and try to finish.return true;}} else if (overflow_state->in_stack_red_zone(addr)) {// Fatal red zone violation. Disable the guard pages and fall through// to handle_unexpected_exception way down below.overflow_state->disable_stack_red_zone();tty->print_raw_cr("An irrecoverable stack overflow has occurred.");} else {// Accessing stack address below sp may cause SEGV if current// thread has MAP_GROWSDOWN stack. This should only happen when// current thread was created by user code with MAP_GROWSDOWN flag// and then attached to VM. See notes in os_linux.cpp.if (thread->osthread()->expanding_stack() == 0) {thread->osthread()->set_expanding_stack();if (os::Linux::manually_expand_stack(thread, addr)) {thread->osthread()->clear_expanding_stack();return true;}thread->osthread()->clear_expanding_stack();} else {fatal("recursive segv. expanding stack.");}}}}if (thread->thread_state() == _thread_in_Java) {// Java thread running in Java code => find exception handler if any// a fault inside compiled code, the interpreter, or a stubif (sig == SIGSEGV && SafepointMechanism::is_poll_address((address)info->si_addr)) {stub = SharedRuntime::get_poll_stub(pc);} else if (sig == SIGBUS) {// BugId 4454115: A read from a MappedByteBuffer can fault// here if the underlying file has been truncated.// Do not crash the VM in such a case.CodeBlob* cb = CodeCache::find_blob_unsafe(pc);CompiledMethod* nm = (cb != NULL) ? cb->as_compiled_method_or_null() : NULL;if ((nm != NULL && nm->has_unsafe_access()) || (thread->doing_unsafe_access() && UnsafeCopyMemory::contains_pc(pc))) {unsafe_access = true;}} else if (sig == SIGSEGV &&MacroAssembler::uses_implicit_null_check(info->si_addr)) {// Determination of interpreter/vtable stub/compiled code null exceptionCodeBlob* cb = CodeCache::find_blob_unsafe(pc);if (cb != NULL) {stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL);}} else if (sig == SIGILL && *(int *)pc == NativeInstruction::zombie_illegal_instruction) {// Zombiestub = SharedRuntime::get_handle_wrong_method_stub();}} else if ((thread->thread_state() == _thread_in_vm ||thread->thread_state() == _thread_in_native) &&sig == SIGBUS && thread->doing_unsafe_access()) {unsafe_access = true;}// jni_fast_Get<Primitive>Field can trap at certain pc's if a GC kicks in// and the heap gets shrunk before the field access.if (sig == SIGSEGV || sig == SIGBUS) {address addr = JNI_FastGetField::find_slowcase_pc(pc);if (addr != (address)-1) {stub = addr;}}}if (unsafe_access && stub == NULL) {// it can be an unsafe access and we haven't found// any other suitable exception reason,// so assume it is an unsafe access.address next_pc = pc + Assembler::InstructionSize;if (UnsafeCopyMemory::contains_pc(pc)) {next_pc = UnsafeCopyMemory::page_error_continue_pc(pc);}
#ifdef __thumb__if (uc->uc_mcontext.arm_cpsr & PSR_T_BIT) {next_pc = (address)((intptr_t)next_pc | 0x1);}
#endifstub = SharedRuntime::handle_unsafe_access(thread, next_pc);}if (stub != NULL) {
#ifdef __thumb__if (uc->uc_mcontext.arm_cpsr & PSR_T_BIT) {intptr_t p = (intptr_t)pc | 0x1;pc = (address)p;// Clear Thumb mode bit if we're redirected into the ARM ISA based codeif (((intptr_t)stub & 0x1) == 0) {uc->uc_mcontext.arm_cpsr &= ~PSR_T_BIT;}} else {// No Thumb2 compiled stubs are triggered from ARM ISA compiled JIT'd code today.// The support needs to be added if that changesassert((((intptr_t)stub & 0x1) == 0), "can't return to Thumb code");}
#endif// save all thread context in case we need to restore itif (thread != NULL) thread->set_saved_exception_pc(pc);os::Posix::ucontext_set_pc(uc, stub);return true;}return false;
}
##源碼
(gdb) bt
#0 SafepointSynchronize::block (thread=0x7ffff02c8200) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/safepoint.cpp:692
#1 0x00007ffff6966332 in SafepointMechanism::process (thread=0x7ffff02c8200, allow_suspend=false)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/safepointMechanism.cpp:125
#2 0x00007ffff5daa6e5 in SafepointMechanism::process_if_requested (thread=0x7ffff02c8200, allow_suspend=false)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/safepointMechanism.inline.hpp:99
#3 0x00007ffff5daaf6d in ThreadBlockInVMPreprocess<InFlightMutexRelease>::~ThreadBlockInVMPreprocess (this=0x7fffd0dfecc8, __in_chrg=<optimized out>)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/interfaceSupport.inline.hpp:264
#4 0x00007ffff5daad70 in ThreadBlockInVM::~ThreadBlockInVM (this=0x7fffd0dfecc0, __in_chrg=<optimized out>)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/interfaceSupport.inline.hpp:289
#5 0x00007ffff696f9ed in ServiceThread::service_thread_entry (jt=0x7ffff02c8200, __the_thread__=0x7ffff02c8200)at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/serviceThread.cpp:191
#6 0x00007ffff6b5d26c in JavaThread::thread_main_inner (this=0x7ffff02c8200) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:1305
#7 0x00007ffff6b5d102 in JavaThread::run (this=0x7ffff02c8200) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:1288
#8 0x00007ffff6b5a805 in Thread::call_run (this=0x7ffff02c8200) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:394
#9 0x00007ffff6874aeb in thread_native_entry (thread=0x7ffff02c8200) at /home/yym/openjdk17/jdk17-master/src/hotspot/os/linux/os_linux.cpp:720
#10 0x00007ffff7c94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#11 0x00007ffff7d26850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81