当前位置: 首页 > news >正文

OpenJDK 17 安全点检查机制深入解析

什么是安全点?

安全点(Safepoint)是 Java 虚拟机中的一个重要概念,它是程序执行过程中的特殊位置,当所有线程都到达安全点时,JVM 才能执行一些需要暂停所有线程的操作,如垃圾回收、代码反优化、线程堆栈遍历等。

解释器中的安全点检查

在 OpenJDK 17 的解释器实现中,安全点检查是通过 InterpreterRuntime::at_safepoint 函数处理的:

cpp

JRT_ENTRY(void, InterpreterRuntime::at_safepoint(JavaThread* current))if (JvmtiExport::should_post_single_step()) {StackWatermarkSet::before_unwind(current);LastFrameAccessor last_frame(current);JvmtiExport::at_single_stepping_point(current, last_frame.method(), last_frame.bcp());}
JRT_END

这个函数主要处理 JVMTI 的单步调试事件。当启用单步调试时,它会在安全点执行必要的操作,包括处理可能被展开的帧。

线程状态转换与安全点

线程在执行过程中需要频繁地在 Java 代码、本地代码和虚拟机代码之间切换,这些转换点都是潜在的安全点检查位置:

cpp

class ThreadInVMfromJava : public ThreadStateTransition {
public:ThreadInVMfromJava(JavaThread* thread, bool check_asyncs = true) : ThreadStateTransition(thread), _check_asyncs(check_asyncs) {trans_from_java(_thread_in_vm);}~ThreadInVMfromJava() {// ... 清理工作trans(_thread_in_vm, _thread_in_Java);if (_thread->has_special_runtime_exit_condition()) _thread->handle_special_runtime_exit_condition(_check_asyncs);}
};

ThreadStateTransition 类管理线程状态转换,确保在转换过程中正确处理安全点。

安全点检查的核心机制

安全点检查的核心实现在 transition 函数中:

cpp

static inline void transition(JavaThread *thread, JavaThreadState from, JavaThreadState to) {assert(thread->thread_state() == from, "coming from wrong thread state");// 检查安全点验证器并清理未处理的 oopsthread->check_possible_safepoint();// 切换到过渡状态确保 VM 线程能看到thread->set_thread_state_fence((JavaThreadState)(from + 1));// 处理安全点请求SafepointMechanism::process_if_requested(thread);thread->set_thread_state(to);
}

这个过程包含几个关键步骤:

  1. 验证当前线程状态

  2. 检查可能的安全点

  3. 设置过渡状态确保可见性

  4. 处理安全点请求

  5. 设置最终状态

安全点处理流程

SafepointMechanism::process_if_requested 是安全点处理的核心:

cpp

void SafepointMechanism::process_if_requested(JavaThread* thread, bool allow_suspend) {if (local_poll_armed(thread)) {process(thread, allow_suspend);}
}

如果本地轮询标志被设置,则进入 process 方法:

cpp

void SafepointMechanism::process(JavaThread *thread, bool allow_suspend) {do {if (global_poll()) {OrderAccess::loadload();SafepointSynchronize::block(thread); // 阻塞线程}StackWatermarkSet::on_safepoint(thread);need_rechecking = thread->handshake_state()->has_operation() && thread->handshake_state()->process_by_self(allow_suspend);} while (need_rechecking);update_poll_values(thread);OrderAccess::cross_modify_fence();
}

线程阻塞机制

当检测到全局安全点请求时,线程会调用 SafepointSynchronize::block 进入阻塞状态:

cpp

void SafepointSynchronize::block(JavaThread *thread) {// 确保线程有效且未终止if (thread->is_terminated()) {thread->block_if_vm_exited();return;}JavaThreadState state = thread->thread_state();thread->frame_anchor()->make_walkable(thread);uint64_t safepoint_id = SafepointSynchronize::safepoint_counter();switch(state) {case _thread_in_vm_trans:case _thread_in_Java:case _thread_in_native_trans:// ... 其他状态thread->safepoint_state()->set_safepoint_id(safepoint_id);thread->set_thread_state_fence(_thread_blocked);_wait_barrier->wait(static_cast<int>(safepoint_id)); // 等待屏障thread->set_thread_state(state);thread->safepoint_state()->reset_safepoint_id();break;default:fatal("Illegal threadstate encountered: %d", state);}
}

解释器中的安全点入口

解释器为每种类型栈状态(TosState)生成安全点入口:

cpp

Interpreter::_safept_entry =EntryPoint(generate_safept_entry_for(atos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(itos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),// ... 其他类型);

这些入口点被设置到安全点表中:

cpp

void TemplateInterpreterGenerator::set_safepoints_for_all_bytes() {for (int i = 0; i < DispatchTable::length; i++) {Bytecodes::Code code = (Bytecodes::Code)i;if (Bytecodes::is_defined(code)) Interpreter::_safept_table.set_entry(code, Interpreter::_safept_entry);}
}

汇编层面的安全点检查

在解释器分发指令时,会进行安全点检查:

cpp

void InterpreterMacroAssembler::dispatch_base(TosState state,address* table,bool verifyoop,bool generate_poll) {// ... 验证和准备工作address* const safepoint_table = Interpreter::safept_table(state);#ifdef _LP64if (table != safepoint_table && generate_poll) {testb(Address(r15_thread, JavaThread::polling_word_offset()), SafepointMechanism::poll_bit());jccb(Assembler::zero, no_safepoint);lea(rscratch1, ExternalAddress((address)safepoint_table));jmpb(dispatch);}
#endif
}

这段汇编代码检查线程本地的轮询字,如果设置了安全点标志,则跳转到安全点处理表。

总结

OpenJDK 17 的安全点机制是一个复杂但高效的系统,它通过以下方式工作:

  1. 状态转换检查:在线程状态转换时检查安全点

  2. 本地轮询:每个线程有一个本地轮询字,定期检查

  3. 全局安全点:当需要所有线程暂停时,设置全局安全点标志

  4. 屏障等待:线程在安全点屏障处等待,直到安全点操作完成

  5. 恢复执行:安全点操作完成后,所有线程恢复执行

这种设计确保了 JVM 能够在需要时高效地暂停所有线程,同时最小化对正常执行的影响。安全点机制的实现涉及解释器、运行时系统、线程管理和底层汇编代码的紧密协作,是 JVM 实现的关键组件之一。

##源码

JRT_ENTRY(void, InterpreterRuntime::at_safepoint(JavaThread* current))// We used to need an explict preserve_arguments here for invoke bytecodes. However,// stack traversal automatically takes care of preserving arguments for invoke, so// this is no longer needed.// JRT_END does an implicit safepoint check, hence we are guaranteed to block// if this is called during a safepointif (JvmtiExport::should_post_single_step()) {// This function is called by the interpreter when single stepping. Such single// stepping could unwind a frame. Then, it is important that we process any frames// that we might return into.StackWatermarkSet::before_unwind(current);// We are called during regular safepoints and when the VM is// single stepping. If any thread is marked for single stepping,// then we may have JVMTI work to do.LastFrameAccessor last_frame(current);JvmtiExport::at_single_stepping_point(current, last_frame.method(), last_frame.bcp());}
JRT_ENDclass ThreadInVMfromJava : public ThreadStateTransition {bool _check_asyncs;public:ThreadInVMfromJava(JavaThread* thread, bool check_asyncs = true) : ThreadStateTransition(thread), _check_asyncs(check_asyncs) {trans_from_java(_thread_in_vm);}~ThreadInVMfromJava()  {if (_thread->stack_overflow_state()->stack_yellow_reserved_zone_disabled()) {_thread->stack_overflow_state()->enable_stack_yellow_reserved_zone();}trans(_thread_in_vm, _thread_in_Java);// We prevent asynchronous exceptions from being installed on return to Java in situations// where we can't tolerate them. See bugs: 4324348, 4854693, 4998314, 5040492, 5050705.if (_thread->has_special_runtime_exit_condition()) _thread->handle_special_runtime_exit_condition(_check_asyncs);}
};void trans(JavaThreadState from, JavaThreadState to)  { transition(_thread, from, to); }// Change threadstate in a manner, so safepoint can detect changes.// Time-critical: called on exit from every runtime routinestatic inline void transition(JavaThread *thread, JavaThreadState from, JavaThreadState to) {assert(from != _thread_in_Java, "use transition_from_java");assert(from != _thread_in_native, "use transition_from_native");assert((from & 1) == 0 && (to & 1) == 0, "odd numbers are transitions states");assert(thread->thread_state() == from, "coming from wrong thread state");// Check NoSafepointVerifier// This also clears unhandled oops if CheckUnhandledOops is used.thread->check_possible_safepoint();// Change to transition state and ensure it is seen by the VM thread.thread->set_thread_state_fence((JavaThreadState)(from + 1));SafepointMechanism::process_if_requested(thread);thread->set_thread_state(to);}void SafepointMechanism::process_if_requested(JavaThread* thread, bool allow_suspend) {// Macos/aarch64 should be in the right state for safepoint (e.g.// deoptimization needs WXWrite).  Crashes caused by the wrong state rarely// happens in practice, making such issues hard to find and reproduce.
#if defined(ASSERT) && defined(__APPLE__) && defined(AARCH64)if (AssertWXAtThreadSync) {thread->assert_wx_state(WXWrite);}
#endifif (local_poll_armed(thread)) {process(thread, allow_suspend);}
}void SafepointMechanism::process(JavaThread *thread, bool allow_suspend) {// Read global poll and has_handshake after local pollOrderAccess::loadload();// local poll already checked, if used.bool need_rechecking;do {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();//yym 注释安全点线程阻塞SafepointSynchronize::block(thread);}// The call to on_safepoint fixes the thread's oops and the first few frames.//// The call has been carefully placed here to cater to a few situations:// 1) After we exit from block after a global poll// 2) After a thread races with the disarming of the global poll and transitions from native/blocked// 3) Before the handshake code is runStackWatermarkSet::on_safepoint(thread);need_rechecking = thread->handshake_state()->has_operation() && thread->handshake_state()->process_by_self(allow_suspend);} while (need_rechecking);update_poll_values(thread);OrderAccess::cross_modify_fence();
}// -------------------------------------------------------------------------------------------------------
// Implementation of Safepoint blocking pointvoid SafepointSynchronize::block(JavaThread *thread) {//yym-gaizao// 获取线程名字并打印oop thread_obj = thread->threadObj();const char* thread_name = "UNKNOWN";char buffer[256];  // 增加缓冲区用于组合字符串if (thread_obj != nullptr) {oop name_oop = java_lang_Thread::name(thread_obj);if (name_oop != nullptr) {thread_name = java_lang_String::as_utf8_string(name_oop);}}// 组合字符串并打印int len = snprintf(buffer, sizeof(buffer), "----SafepointSynchronize::block----Thread %s \n", thread_name);write(STDERR_FILENO, buffer, len);// tty->print_cr("@@@@yym%%%%----SafepointSynchronize::block----Thread: %s", thread_name);//信号函数处理不安全函数 异常崩溃assert(thread != NULL, "thread must be set");// Threads shouldn't block if they are in the middle of printing, but...ttyLocker::break_tty_lock_for_safepoint(os::current_thread_id());// Only bail from the block() call if the thread is gone from the// thread list; starting to exit should still block.if (thread->is_terminated()) {// block current thread if we come here from native code when VM is gonethread->block_if_vm_exited();// otherwise do nothingreturn;}JavaThreadState state = thread->thread_state();thread->frame_anchor()->make_walkable(thread);uint64_t safepoint_id = SafepointSynchronize::safepoint_counter();// Check that we have a valid thread_state at this pointswitch(state) {case _thread_in_vm_trans:case _thread_in_Java:        // From compiled codecase _thread_in_native_trans:case _thread_blocked_trans:case _thread_new_trans:// We have no idea where the VMThread is, it might even be at next safepoint.// So we can miss this poll, but stop at next.// Load dependent store, it must not pass loading of safepoint_id.thread->safepoint_state()->set_safepoint_id(safepoint_id); // Release store// This part we can skip if we notice we miss or are in a future safepoint.OrderAccess::storestore();// Load in wait barrier should not float upthread->set_thread_state_fence(_thread_blocked);_wait_barrier->wait(static_cast<int>(safepoint_id));assert(_state != _synchronized, "Can't be");// If barrier is disarmed stop store from floating above loads in barrier.OrderAccess::loadstore();thread->set_thread_state(state);// Then we reset the safepoint id to inactive.thread->safepoint_state()->reset_safepoint_id(); // Release storeOrderAccess::fence();break;default:fatal("Illegal threadstate encountered: %d", state);}guarantee(thread->safepoint_state()->get_safepoint_id() == InactiveSafepointCounter,"The safepoint id should be set only in block path");// cross_modify_fence is done by SafepointMechanism::process_if_requested// which is the only caller here.
}{ CodeletMark cm(_masm, "safepoint entry points");Interpreter::_safept_entry =EntryPoint(generate_safept_entry_for(atos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(itos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(ltos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(ftos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(dtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(vtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)));}void TemplateInterpreterGenerator::set_safepoints_for_all_bytes() {for (int i = 0; i < DispatchTable::length; i++) {Bytecodes::Code code = (Bytecodes::Code)i;if (Bytecodes::is_defined(code)) Interpreter::_safept_table.set_entry(code, Interpreter::_safept_entry);}
}static address*   safept_table(TosState state)                { return _safept_table.table_for(state); }void InterpreterMacroAssembler::dispatch_base(TosState state,address* table,bool verifyoop,bool generate_poll) {verify_FPU(1, state);if (VerifyActivationFrameSize) {Label L;mov(rcx, rbp);subptr(rcx, rsp);int32_t min_frame_size =(frame::link_offset - frame::interpreter_frame_initial_sp_offset) *wordSize;cmpptr(rcx, (int32_t)min_frame_size);jcc(Assembler::greaterEqual, L);stop("broken stack frame");bind(L);}if (verifyoop) {interp_verify_oop(rax, state);}address* const safepoint_table = Interpreter::safept_table(state);
#ifdef _LP64Label no_safepoint, dispatch;if (table != safepoint_table && generate_poll) {NOT_PRODUCT(block_comment("Thread-local Safepoint poll"));testb(Address(r15_thread, JavaThread::polling_word_offset()), SafepointMechanism::poll_bit());jccb(Assembler::zero, no_safepoint);lea(rscratch1, ExternalAddress((address)safepoint_table));jmpb(dispatch);}bind(no_safepoint);lea(rscratch1, ExternalAddress((address)table));bind(dispatch);jmp(Address(rscratch1, rbx, Address::times_8));#elseAddress index(noreg, rbx, Address::times_ptr);if (table != safepoint_table && generate_poll) {NOT_PRODUCT(block_comment("Thread-local Safepoint poll"));Label no_safepoint;const Register thread = rcx;get_thread(thread);testb(Address(thread, JavaThread::polling_word_offset()), SafepointMechanism::poll_bit());jccb(Assembler::zero, no_safepoint);ArrayAddress dispatch_addr(ExternalAddress((address)safepoint_table), index);jump(dispatch_addr);bind(no_safepoint);}{ArrayAddress dispatch_addr(ExternalAddress((address)table), index);jump(dispatch_addr);}
#endif // _LP64
}

http://www.dtcms.com/a/346623.html

相关文章:

  • 【AI基础:神经网络】16、神经网络的生理学根基:从人脑结构到AI架构,揭秘道法自然的智能密码
  • Photoshop CS6精简版轻量级,Photoshop CS6绿色免安装,Photoshop CS6安装教程
  • Kafka 概念与概述
  • AI热点周报(8.17~8.23):Pixel 10“AI周”、DeepSeek V3.1发布,英伟达再起波澜?
  • Kafka Streams vs Apache Flink vs Apache Storm: 实时流处理方案对比与选型建议
  • 何为‘口业’,怎么看待它
  • 轻量化设计·全要素监测——新一代便携式气象站赋能户外科研与应急
  • Elasticsearch Persistence(elasticsearch-persistence)仓储模式实战
  • 改华为智能插座为mqtt本地控制
  • 强光干扰与密集场景下工服识别准确率↑89%!陌讯多模态融合算法在安全生产中的实战优化
  • 华为/思科/H3C/锐捷操作系统操作指南
  • Mybatis面试题分享
  • 【网安干货】--操作系统基础(上)
  • 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第五章整理
  • Python爬虫实战:研究WSL技术,构建跨平台数据采集和分析系统
  • 攻防世界—Confusion1—(模板注入ssti)
  • IPsec
  • selenium采集数据怎么应对反爬机制?
  • C++算法·进制转换
  • 2025/8/23c++++++++
  • kafaka知识要点
  • Spring Boot 3为何强制要求Java 17?
  • 【modbus】数据采集系统
  • 微服务概述1
  • Elasticsearch Ruby 客户端 Bulk Scroll Helpers 实战指南
  • 【Redis 进阶】----主从复制(重点理解流程和原理)
  • 【Tech Arch】Apache Flume海量日志采集的高速公路
  • 如何在 Spring Boot 中安全读取账号密码等
  • SpringBoot3整合dubbo3客户端【最佳实践】
  • 【204页PPT】某著名企业信息化规划方案(附下载方式)