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

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);
}

相关文章:

  • 李臻20242817_安全文件传输系统项目报告_第12周
  • 【Java ee初阶】jvm(3)
  • ARM A64 STR指令
  • Java大厂面试:从Web框架到微服务技术的场景化提问与解析
  • Java基础知识总结(超详细整理)
  • 发布时将多个bpl 打包成一个bpl的方法,或者说:不需要vcl60.bpl情况下 18.5K的exe 照常可以运行。
  • c++学习之--- list
  • Nordic 的RTC(Real-time counter)的介绍
  • 虚幻引擎5-Unreal Engine笔记之Default Pawn与GamMode、Camera的关系
  • IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + IoT Link 插件
  • SQL脚本规范
  • 【OpenCV基础 1】几何变换、形态学处理、阈值分割、区域提取和脱敏处理
  • 2025.5.12-2025.5.18:开始练习英语口语
  • httpx[http2] 和 httpx 的核心区别及使用场景如下
  • 01 Nginx安装及基本配置
  • PostGIS实现矢量数据转栅格数据【ST_AsRaster】
  • pnpm项目内网迁移
  • minicom串口调试助手
  • Linux云计算训练营笔记day10(MySQL数据库)
  • MapReduce-WordCount实现按照value降序排序、字符小写、识别不同标点
  • 国际观察丨美中东政策生变,以色列面临艰难选择
  • 雅典卫城上空现“巨鞋”形状无人机群,希腊下令彻查
  • 消息人士称俄方反对美国代表参加俄乌直接会谈
  • 体坛联播|巴萨提前2轮西甲夺冠,郑钦文不敌高芙止步4强
  • 押井守在30年前创造的虚拟世界何以比当下更超前?
  • 问责!美国海军对“杜鲁门”号航母一系列事故展开调查