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

jvm安全点(四)openjdk17 c++源码垃圾回收之安全点轮询页内存设置不可访问

轮询页的不可访问性是在 ​​JVM 启动初始化阶段静态设置​​ 的,具体由以下函数完成:


​核心函数:os::protect_memory

 

bool os::protect_memory(char* addr, size_t bytes, ProtType prot, bool is_committed) { unsigned int p = 0; switch (prot) { case MEM_PROT_NONE: p = PROT_NONE; break; // 设置为不可访问 case MEM_PROT_READ: p = PROT_READ; break; // ...其他权限设置 } return linux_mprotect(addr, bytes, p); // 调用系统调用修改内存权限 }

  • ​功能​​:通过 mprotect 系统调用修改指定内存区域的权限。
  • ​调用链​​:在 JVM 启动时由 SafepointMechanism::default_initialize() 调用。

​初始化阶段设置轮询页权限​

在 SafepointMechanism::default_initialize() 中:

 

// 分配两个连续页:bad_page(不可访问)和 good_page(可读) os::protect_memory(bad_page, page_size, os::MEM_PROT_NONE); // 设置 bad_page 不可访问 os::protect_memory(good_page, page_size, os::MEM_PROT_READ); // 设置 good_page 可读

  • ​静态配置​​:此时 bad_page 被永久设置为 ​​不可访问​​,good_page 设置为可读。
  • ​后续使用​​:
    • 当需要进入安全点时,JVM 让线程访问 bad_page(通过修改线程的轮询地址),触发 ​​SIGSEGV 信号​​。
    • 安全点结束后,线程恢复访问 good_page

​动态触发安全点的机制​

在安全点开始时,通过以下函数 ​​切换线程的轮询地址​​ 到 bad_page(而不是修改内存权限):

 

void SafepointMechanism::arm_local_poll(JavaThread* thread) { // 将线程的轮询地址设为 _poll_page_armed_value(即 bad_page) thread->set_polling_page(_poll_page_armed_value); }

  • ​不修改内存权限​​:轮询页的权限在初始化时已固定,JVM 通过切换线程访问的目标地址(bad_page 或 good_page)控制安全点触发。

​总结​

  • ​设置轮询页不可访问的函数​​:os::protect_memory(在 JVM 启动时调用)。
  • ​触发安全点的机制​​:通过 arm_local_poll() 将线程的轮询地址指向预先不可访问的 bad_page,而非动态修改内存权限。

##源码

/ Set protections specified
bool os::protect_memory(char* addr, size_t bytes, ProtType prot,bool is_committed) {unsigned int p = 0;switch (prot) {case MEM_PROT_NONE: p = PROT_NONE; break;case MEM_PROT_READ: p = PROT_READ; break;case MEM_PROT_RW:   p = PROT_READ|PROT_WRITE; break;case MEM_PROT_RWX:  p = PROT_READ|PROT_WRITE|PROT_EXEC; break;default:ShouldNotReachHere();}// is_committed is unused.return linux_mprotect(addr, bytes, p);
}int SafepointSynchronize::synchronize_threads(jlong safepoint_limit_time, int nof_threads, int* initial_running)
{JavaThreadIteratorWithHandle jtiwh;#ifdef ASSERTfor (; JavaThread *cur = jtiwh.next(); ) {assert(cur->safepoint_state()->is_running(), "Illegal initial state");}jtiwh.rewind();
#endif // ASSERT// Iterate through all threads until it has been determined how to stop them all at a safepoint.int still_running = nof_threads;ThreadSafepointState *tss_head = NULL;ThreadSafepointState **p_prev = &tss_head;for (; JavaThread *cur = jtiwh.next(); ) {ThreadSafepointState *cur_tss = cur->safepoint_state();assert(cur_tss->get_next() == NULL, "Must be NULL");if (thread_not_running(cur_tss)) {--still_running;} else {*p_prev = cur_tss;p_prev = cur_tss->next_ptr();}}*p_prev = NULL;DEBUG_ONLY(assert_list_is_valid(tss_head, still_running);)*initial_running = still_running;// If there is no thread still running, we are already done.if (still_running <= 0) {assert(tss_head == NULL, "Must be empty");return 1;}int iterations = 1; // The first iteration is above.int64_t start_time = os::javaTimeNanos();do {// Check if this has taken too long:if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) {print_safepoint_timeout();}p_prev = &tss_head;ThreadSafepointState *cur_tss = tss_head;while (cur_tss != NULL) {assert(cur_tss->is_running(), "Illegal initial state");if (thread_not_running(cur_tss)) {--still_running;*p_prev = NULL;ThreadSafepointState *tmp = cur_tss;cur_tss = cur_tss->get_next();tmp->set_next(NULL);} else {*p_prev = cur_tss;p_prev = cur_tss->next_ptr();cur_tss = cur_tss->get_next();}}DEBUG_ONLY(assert_list_is_valid(tss_head, still_running);)if (still_running > 0) {back_off(start_time);}iterations++;} while (still_running > 0);assert(tss_head == NULL, "Must be empty");return iterations;
}void SafepointSynchronize::arm_safepoint() {// Begin the process of bringing the system to a safepoint.// Java threads can be in several different states and are// stopped by different mechanisms:////  1. Running interpreted//     When executing branching/returning byte codes interpreter//     checks if the poll is armed, if so blocks in SS::block().//  2. Running in native code//     When returning from the native code, a Java thread must check//     the safepoint _state to see if we must block.  If the//     VM thread sees a Java thread in native, it does//     not wait for this thread to block.  The order of the memory//     writes and reads of both the safepoint state and the Java//     threads state is critical.  In order to guarantee that the//     memory writes are serialized with respect to each other,//     the VM thread issues a memory barrier instruction.//  3. Running compiled Code//     Compiled code reads the local polling page that//     is set to fault if we are trying to get to a safepoint.//  4. Blocked//     A thread which is blocked will not be allowed to return from the//     block condition until the safepoint operation is complete.//  5. In VM or Transitioning between states//     If a Java thread is currently running in the VM or transitioning//     between states, the safepointing code will poll the thread state//     until the thread blocks itself when it attempts transitions to a//     new state or locking a safepoint checked monitor.// We must never miss a thread with correct safepoint id, so we must make sure we arm// the wait barrier for the next safepoint id/counter.// Arming must be done after resetting _current_jni_active_count, _waiting_to_block._wait_barrier->arm(static_cast<int>(_safepoint_counter + 1));assert((_safepoint_counter & 0x1) == 0, "must be even");// The store to _safepoint_counter must happen after any stores in arming.Atomic::release_store(&_safepoint_counter, _safepoint_counter + 1);// We are synchronizingOrderAccess::storestore(); // Ordered with _safepoint_counter_state = _synchronizing;// Arming the per thread poll while having _state != _not_synchronized means safepointinglog_trace(safepoint)("Setting thread local yield flag for threads");OrderAccess::storestore(); // storestore, global state -> local statefor (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {// Make sure the threads start polling, it is time to yield.SafepointMechanism::arm_local_poll(cur);}OrderAccess::fence(); // storestore|storeload, global state -> local state
}// Roll all threads forward to a safepoint and suspend them all
void SafepointSynchronize::begin() {assert(Thread::current()->is_VM_thread(), "Only VM thread may execute a safepoint");EventSafepointBegin begin_event;SafepointTracing::begin(VMThread::vm_op_type());Universe::heap()->safepoint_synchronize_begin();// By getting the Threads_lock, we assure that no threads are about to start or// exit. It is released again in SafepointSynchronize::end().Threads_lock->lock();assert( _state == _not_synchronized, "trying to safepoint synchronize with wrong state");int nof_threads = Threads::number_of_threads();_nof_threads_hit_polling_page = 0;log_debug(safepoint)("Safepoint synchronization initiated using %s wait barrier. (%d threads)", _wait_barrier->description(), nof_threads);// Reset the count of active JNI critical threads_current_jni_active_count = 0;// Set number of threads to wait for_waiting_to_block = nof_threads;jlong safepoint_limit_time = 0;if (SafepointTimeout) {// Set the limit time, so that it can be compared to see if this has taken// too long to complete.safepoint_limit_time = SafepointTracing::start_of_safepoint() + (jlong)SafepointTimeoutDelay * (NANOUNITS / MILLIUNITS);timeout_error_printed = false;}EventSafepointStateSynchronization sync_event;int initial_running = 0;// Arms the safepoint, _current_jni_active_count and _waiting_to_block must be set before.arm_safepoint();// Will spin until all threads are safe.int iterations = synchronize_threads(safepoint_limit_time, nof_threads, &initial_running);assert(_waiting_to_block == 0, "No thread should be running");#ifndef PRODUCT// Mark all threadsif (VerifyCrossModifyFence) {JavaThreadIteratorWithHandle jtiwh;for (; JavaThread *cur = jtiwh.next(); ) {cur->set_requires_cross_modify_fence(true);}}if (safepoint_limit_time != 0) {jlong current_time = os::javaTimeNanos();if (safepoint_limit_time < current_time) {log_warning(safepoint)("# SafepointSynchronize: Finished after "INT64_FORMAT_W(6) " ms",(int64_t)(current_time - SafepointTracing::start_of_safepoint()) / (NANOUNITS / MILLIUNITS));}}
#endifassert(Threads_lock->owned_by_self(), "must hold Threads_lock");// Record state_state = _synchronized;OrderAccess::fence();// Set the new id++_safepoint_id;#ifdef ASSERT// Make sure all the threads were visited.for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {assert(cur->was_visited_for_critical_count(_safepoint_counter), "missed a thread");}
#endif // ASSERT// Update the count of active JNI critical regionsGCLocker::set_jni_lock_count(_current_jni_active_count);post_safepoint_synchronize_event(sync_event,_safepoint_id,initial_running,_waiting_to_block, iterations);SafepointTracing::synchronized(nof_threads, initial_running, _nof_threads_hit_polling_page);// We do the safepoint cleanup first since a GC related safepoint// needs cleanup to be completed before running the GC op.EventSafepointCleanup cleanup_event;do_cleanup_tasks();post_safepoint_cleanup_event(cleanup_event, _safepoint_id);post_safepoint_begin_event(begin_event, _safepoint_id, nof_threads, _current_jni_active_count);SafepointTracing::cleanup();
}void SafepointMechanism::default_initialize() {// Poll bit values_poll_word_armed_value    = poll_bit();_poll_word_disarmed_value = ~_poll_word_armed_value;bool poll_bit_only = false;#ifdef USE_POLL_BIT_ONLYpoll_bit_only = USE_POLL_BIT_ONLY;
#endifif (poll_bit_only) {_poll_page_armed_value    = poll_bit();_poll_page_disarmed_value = 0;} else {// Polling pageconst 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;}
}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);
}// Caller is responsible for using a memory barrier if needed.
inline void SafepointMechanism::ThreadData::set_polling_page(uintptr_t poll_value) {Atomic::store(&_polling_page, poll_value);
}

http://www.xdnf.cn/news/7142.html

相关文章:

  • 前端图片上传组件实战:从动态销毁Input到全屏预览的全功能实现
  • 备份C#的两个类
  • 【DAY22】 复习日
  • 三、高级攻击工具与框架
  • React Flow 边的基础知识与示例:从基本属性到代码实例详解
  • 飞机飞行控制系统补偿模型辨识报告
  • HarmonyOS AVPlayer 音频播放器
  • 【2025软考高级架构师】——2022年11月份真题与解析
  • 【方法论】如何构建金字塔框架
  • C++ for QWidget:connect(连接)
  • C++ asio网络编程(8)处理粘包问题
  • Java IO及Netty框架学习小结
  • 学习黑客 http 响应头
  • Spark 基础自定义分区器
  • 游戏:英雄联盟游戏开发代码(谢苏)
  • 互联网大厂Java面试场景:从简单到复杂的技术深度解析
  • Java注解篇:@CrossOrigin
  • 鸿蒙AI开发:10-多模态大模型与原子化服务的集成
  • 大学之大:墨西哥国立自治大学2025.5.18
  • STM32项目实战:ADC采集
  • [原创工具] 小说写作软件
  • java springMVC+MyBatis项目1,服务端处理json,RequestBody注解,Form表单发送,JavaScript发送
  • 【量子计算与云架构】加密与算法革新展望
  • Python format()函数高级字符串格式化详解
  • LG P4722 LOJ 127 【模板】最大流 加强版 Solution
  • C语言练手磨时间
  • 编程速递:适用于 Delphi 12.3 的 FMX Linux 现已推出
  • C++面试2——C与C++的关系
  • 12.输出常量的两个小扩展
  • leetcode hot100刷题日记——2.字母异位词分组