JVM安全点轮询汇编函数解析

OpenJDK 17 源码的实现逻辑,handle_polling_page_exception 函数在方法返回时的调用流程如下:

调用流程分析:

  1. 栈水印检查触发跳转

    • 当线程执行方法返回前的安全点轮询时(MacroAssembler::safepoint_pollat_return=true

    • 如果栈指针超出安全区域(cmpptr 比较结果为 above),会跳转到关联的 slow_path 标签

    • 这个 slow_path 标签由 C2SafepointPollStub 管理

  2. 桩代码生成

    • C2SafepointPollStubTable::emit() 遍历所有安全点条目

    • 为每个条目调用 emit_stub() 生成桩代码

    cpp

    复制代码
    void emit_stub(MacroAssembler& masm, C2SafepointPollStub* entry) const {
      select_emit_stub<VM_Version::supports_stack_watermark_barrier()>(masm, entry);
    }
  3. 桩代码核心逻辑

    • emit_stub_impl() 中生成实际汇编代码:

      cpp

      复制代码
      __ bind(entry->_stub_label);
      // 计算安全点返回地址
      InternalAddress safepoint_pc(...);
      
      // 保存返回地址到线程控制块
      __ lea(rscratch1, safepoint_pc);
      __ movptr(Address(r15_thread, JavaThread::saved_exception_pc_offset()), rscratch1);
      
      // 跳转到异常处理入口
      __ jump(callback_addr); // callback_addr = polling_page_return_handler_blob
  4. 处理函数绑定

    • polling_page_return_handler_blob 在 JVM 初始化时创建:

      cpp

      复制代码
      _polling_page_return_handler_blob = generate_handler_blob(
          CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception),
          POLL_AT_RETURN
      );
    • 该 blob 将 handle_polling_page_exception 函数地址嵌入到生成的机器代码中

关键调用路径:

text

复制代码
执行线程 → 方法返回前的轮询检查
       │
       ├─ 栈水印检查失败 → 跳转到 slow_path (C2SafepointPollStub)
       │    │
       │    └─ 桩代码执行:
       │        1. 保存返回地址到 JavaThread::saved_exception_pc_offset()
       │        2. 跳转到 polling_page_return_handler_blob
       │             │
       │             └─ 执行 handle_polling_page_exception()
       │                  │
       │                  └─ 安全点同步处理
       │
       └─ 正常继续执行

技术要点:

  1. 上下文保存

    • 桩代码通过 saved_exception_pc_offset() 保存原始返回地址

    • 确保异常处理后能正确返回到原执行点

  2. 平台适配

    • 使用 select_emit_stub 模板实现条件编译

    • 仅当 VM_Version::supports_stack_watermark_barrier() 为 true 时生成桩代码

  3. 异常处理机制

    cpp

    复制代码
    void SafepointSynchronize::handle_polling_page_exception(JavaThread* thread) {
      // 1. 检查安全点状态
      // 2. 阻塞线程直到安全点操作完成
      // 3. 恢复线程执行(通过 saved_exception_pc 返回)
    }
  4. 水印检查优化

    • 在 nmethod 中使用 rsp 而非 rbp,因为:

      cpp

      复制代码
      // 当 in_nmethod 时,栈指针已在轮询前递增
      cmpptr(in_nmethod ? rsp : rbp, ...);

总结:

当线程在方法返回前检测到栈水印越界时,会通过预先生成的桩代码跳转到 handle_polling_page_exception 函数。该函数负责将线程挂起直到安全点操作完成,然后通过保存的返回地址恢复执行。这种机制实现了高效的安全点协作,同时保证了栈收缩时的内存安全。

##源码

cpp 复制代码
#define __ masm.
void C2SafepointPollStubTable::emit_stub_impl(MacroAssembler& masm, C2SafepointPollStub* entry) const {
  assert(SharedRuntime::polling_page_return_handler_blob() != NULL,
         "polling page return stub not created yet");
  address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point();

  RuntimeAddress callback_addr(stub);

  __ bind(entry->_stub_label);
  InternalAddress safepoint_pc(masm.pc() - masm.offset() + entry->_safepoint_offset);
#ifdef _LP64
  __ lea(rscratch1, safepoint_pc);
  __ movptr(Address(r15_thread, JavaThread::saved_exception_pc_offset()), rscratch1);
#else
  const Register tmp1 = rcx;
  const Register tmp2 = rdx;
  __ push(tmp1);
  __ push(tmp2);

  __ lea(tmp1, safepoint_pc);
  __ get_thread(tmp2);
  __ movptr(Address(tmp2, JavaThread::saved_exception_pc_offset()), tmp1);

  __ pop(tmp2);
  __ pop(tmp1);
#endif
  __ jump(callback_addr);
}
#undef __

void C2SafepointPollStubTable::emit(CodeBuffer& cb) {
  MacroAssembler masm(&cb);
  for (int i = _safepoints.length() - 1; i >= 0; i--) {
    // Make sure there is enough space in the code buffer
    if (cb.insts()->maybe_expand_to_ensure_remaining(PhaseOutput::MAX_inst_size) && cb.blob() == NULL) {
      ciEnv::current()->record_failure("CodeCache is full");
      return;
    }

    C2SafepointPollStub* entry = _safepoints.at(i);
    emit_stub(masm, entry);
  }
}

  // The selection logic below relieves the need to add dummy files to unsupported platforms.
  template <bool enabled>
  typename EnableIf<enabled>::type
  select_emit_stub(MacroAssembler& masm, C2SafepointPollStub* entry) const {
    emit_stub_impl(masm, entry);
  }

  template <bool enabled>
  typename EnableIf<!enabled>::type
  select_emit_stub(MacroAssembler& masm, C2SafepointPollStub* entry) const {}

  void emit_stub(MacroAssembler& masm, C2SafepointPollStub* entry) const {
    select_emit_stub<VM_Version::supports_stack_watermark_barrier()>(masm, entry);
  }

_polling_page_return_handler_blob    = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_RETURN);

static SafepointBlob* polling_page_return_handler_blob()     { return _polling_page_return_handler_blob; }

##gdb堆栈

cpp 复制代码
(gdb) bt
#0  C2SafepointPollStubTable::emit (this=0x7fffd0bfbb98, cb=...) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/output.cpp:237
#1  0x00007ffff6896363 in PhaseOutput::fill_buffer (this=0x7fffd0bfb950, cb=0x7fffd0bfb970, blk_starts=0x7fffb002a750)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/output.cpp:1824
#2  0x00007ffff6890444 in PhaseOutput::Output (this=0x7fffd0bfb950) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/output.cpp:432
#3  0x00007ffff602fe6b in Compile::Code_Gen (this=0x7fffd0bfde90) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/compile.cpp:2775
#4  0x00007ffff6027214 in Compile::Compile (this=0x7fffd0bfde90, ci_env=0x7fffd0bfeac0, generator=0x7ffff6959ff0 <OptoRuntime::new_instance_Type()>,
    stub_function=0x7ffff6958c68 <OptoRuntime::new_instance_C(Klass*, JavaThread*)> "\363\017\036\372UH\211\345H\203\354`H\211}\250H\211u\240H\213U\240H\215E\320H\211\326H\211\307\350\067\204N\377H\213M\240H\215E\340\272\001", stub_name=0x7ffff72815ea "_new_instance_Java", is_fancy_jump=0, pass_tls=true, return_pc=false, directive=0x7ffff01b1f90)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/compile.cpp:925
#5  0x00007ffff6958b70 in OptoRuntime::generate_stub (env=0x7fffd0bfeac0, gen=0x7ffff6959ff0 <OptoRuntime::new_instance_Type()>,
    C_function=0x7ffff6958c68 <OptoRuntime::new_instance_C(Klass*, JavaThread*)> "\363\017\036\372UH\211\345H\203\354`H\211}\250H\211u\240H\213U\240H\215E\320H\211\326H\211\307\350\067\204N\377H\213M\240H\215E\340\272\001", name=0x7ffff72815ea "_new_instance_Java", is_fancy_jump=0, pass_tls=true, return_pc=false)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/runtime.cpp:171
#6  0x00007ffff6958625 in OptoRuntime::generate (env=0x7fffd0bfeac0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/runtime.cpp:139
#7  0x00007ffff5f07753 in C2Compiler::init_c2_runtime () at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/c2compiler.cpp:78
#8  0x00007ffff5f077d7 in C2Compiler::initialize (this=0x7ffff02c01f0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/opto/c2compiler.cpp:91
#9  0x00007ffff6043287 in CompileBroker::init_compiler_runtime () at /home/yym/openjdk17/jdk17-master/src/hotspot/share/compiler/compileBroker.cpp:1801
#10 0x00007ffff6043806 in CompileBroker::compiler_thread_loop () at /home/yym/openjdk17/jdk17-master/src/hotspot/share/compiler/compileBroker.cpp:1938
#11 0x00007ffff6065c02 in CompilerThread::thread_entry (thread=0x7ffff02c08c0, __the_thread__=0x7ffff02c08c0)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/compiler/compilerThread.cpp:59
#12 0x00007ffff6b5e498 in JavaThread::thread_main_inner (this=0x7ffff02c08c0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:1305
#13 0x00007ffff6b5e32e in JavaThread::run (this=0x7ffff02c08c0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:1288
#14 0x00007ffff6b5ba31 in Thread::call_run (this=0x7ffff02c08c0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:394
#15 0x00007ffff68752bb in thread_native_entry (thread=0x7ffff02c08c0) at /home/yym/openjdk17/jdk17-master/src/hotspot/os/linux/os_linux.cpp:720
#16 0x00007ffff7c94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#17 0x00007ffff7d26850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

##gdb堆栈

cpp 复制代码
Thread 15 "C1 CompilerThre" hit Breakpoint 1, SharedRuntime::polling_page_return_handler_blob () at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/sharedRuntime.hpp:250
250       static SafepointBlob* polling_page_return_handler_blob()     { return _polling_page_return_handler_blob; }
(gdb) bt
#0  SharedRuntime::polling_page_return_handler_blob () at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/sharedRuntime.hpp:250
#1  0x00007ffff5e14d46 in C1SafepointPollStub::emit_code (this=0x7ffff0385490, ce=0x7fffd0afe0f0) at /home/yym/openjdk17/jdk17-master/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp:104
#2  0x00007ffff5e64987 in LIR_Assembler::emit_stubs (this=0x7fffd0afe0f0, stub_list=0x7fffa409c5f0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/c1/c1_LIRAssembler.cpp:150
#3  0x00007ffff5e649ff in LIR_Assembler::emit_slow_case_stubs (this=0x7fffd0afe0f0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/c1/c1_LIRAssembler.cpp:159
#4  0x00007ffff5e19a9d in Compilation::emit_code_epilog (this=0x7fffd0afe4d0, assembler=0x7fffd0afe0f0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/c1/c1_Compilation.cpp:291
#5  0x00007ffff5e19e18 in Compilation::emit_code_body (this=0x7fffd0afe4d0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/c1/c1_Compilation.cpp:355
#6  0x00007ffff5e1a12a in Compilation::compile_java_method (this=0x7fffd0afe4d0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/c1/c1_Compilation.cpp:404
#7  0x00007ffff5e1a470 in Compilation::compile_method (this=0x7fffd0afe4d0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/c1/c1_Compilation.cpp:460
#8  0x00007ffff5e1ac88 in Compilation::Compilation (this=0x7fffd0afe4d0, compiler=0x7ffff0231910, env=0x7fffd0afe9f0, method=0x7fffa40922b0, osr_bci=-1, buffer_blob=0x7fffe111cf90,
    install_code=true, directive=0x7ffff01b56b0) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/c1/c1_Compilation.cpp:584
#9  0x00007ffff5e1f184 in Compiler::compile_method (this=0x7ffff0231910, env=0x7fffd0afe9f0, method=0x7fffa40922b0, entry_bci=-1, install_code=true, directive=0x7ffff01b56b0)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/c1/c1_Compiler.cpp:247
#10 0x00007ffff6044f06 in CompileBroker::invoke_compiler_on_method (task=0x7ffff030e790) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/compiler/compileBroker.cpp:2312
#11 0x00007ffff6043a81 in CompileBroker::compiler_thread_loop () at /home/yym/openjdk17/jdk17-master/src/hotspot/share/compiler/compileBroker.cpp:1985
#12 0x00007ffff6065c02 in CompilerThread::thread_entry (thread=0x7ffff02c2340, __the_thread__=0x7ffff02c2340)
    at /home/yym/openjdk17/jdk17-master/src/hotspot/share/compiler/compilerThread.cpp:59
#13 0x00007ffff6b5e498 in JavaThread::thread_main_inner (this=0x7ffff02c2340) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:1305
#14 0x00007ffff6b5e32e in JavaThread::run (this=0x7ffff02c2340) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:1288
#15 0x00007ffff6b5ba31 in Thread::call_run (this=0x7ffff02c2340) at /home/yym/openjdk17/jdk17-master/src/hotspot/share/runtime/thread.cpp:394
#16 0x00007ffff68752bb in thread_native_entry (thread=0x7ffff02c2340) at /home/yym/openjdk17/jdk17-master/src/hotspot/os/linux/os_linux.cpp:720
#17 0x00007ffff7c94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#18 0x00007ffff7d26850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
相关推荐
wanhengidc35 分钟前
在云手机中游戏可以自动更新吗?
运维·科技·安全·游戏·智能手机
wanhengidc41 分钟前
云端虚拟手机:云手机的原理是什么?
运维·安全·游戏·智能手机
介一安全1 小时前
【Web安全】CRLF注入攻击深度解析:原理、场景与安全测试防御指南
安全·web安全·网络安全·代码审计·安全性测试
drjava_20191 小时前
DeFi代币授权安全指南:保护您的数字资产
安全·区块链
Akshsjsjenjd1 小时前
Ansible 变量与加密文件全解析:从基础定义到安全实践
前端·安全·ansible
云盾安全防护2 小时前
WAF与CDN在网络安全中的协同作用
网络·安全·web安全
喜葵4 小时前
前端安全防护深度实践:从XSS到CSRF的完整安全解决方案
前端·安全·xss
翻斗花园刘大胆4 小时前
JavaSE之String 与 StringBuilder 全面解析(附实例代码)
java·开发语言·jvm·git·java-ee·intellij-idea·html5
Sh3r10ck5 小时前
【Hack The Box】Puppy Write Up
安全
星梦清河5 小时前
宋红康 JVM 笔记 Day09|方法区
jvm·笔记