尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
JVM停顿时机:从Stop The World到安全点与OopMap
如果你在JVM调优或者看GC日志时见过“Stop The World”“OopMap”“安全点”这几个词大概率和我一样一开始把它们当成三个独立概念。实际不是它们是一条链路垃圾回收器要暂停应用暂停的瞬间必须在安全点而安全点上能快速判断栈里哪些引用是根靠的就是OopMap。我在排查某一次Full GC耗时异常时顺着这条链路走了一遍才真正把JVM的停顿机制串起来。这篇文章就把这条链路掰开讲顺带附上一些可落地的排查经验适合刚入门JVM内存管理的人也适合做过调优但老被术语绕晕的老手。1. 先从Stop The World说起JVM为什么要按暂停键1.1 一次GC里的“没人动”窗口Stop The World简称STW很多人把它当成GC的代名词。严格来说它是JVM触发某个“VM操作”时把所有Java业务线程冻结住的那段窗口。在这段时间里只有JVM内部的垃圾回收器或者其他管理线程在跑应用代码一个字节都执行不了。为什么非要冻结因为垃圾回收最核心的动作是从根对象出发顺着引用关系找一遍活着的对象。这个“找”的过程假设对象图是静态的。假如GC线程在遍历对象图业务线程还在同时new一个新对象、或者把某个字段指向另一个对象那么同一份堆内存就有两套状态遍历结果和实际存活集合可能完全对不上甚至把还活着的对象当成垃圾回收掉。我常用一个整理书架的类比你要统计书架上哪些书还需要就得让所有人暂停取书和放书。谁要敢在这个瞬间动一下书统计结果就是错的。STW就是这个“暂停所有人”的动作只是JVM暂停的不是人是线程。读到这里有人会问并发收集器CMS、G1不是可以和应用并发跑吗注意并发收集器的“并发”不是全程并发。G1的初始标记、最终标记、清理等环节早期是有STW的CMS虽然并发标记但初始标记和重新标记也停顿。真正把停顿压到极短甚至接近固定值的ZGC、Shenandoah是后来把标记、整理的大部分工作做成并发但涉及根节点枚举、栈扫描等操作依然会有一个很短的同步点。1.2 不止GC会触发安全点停顿STW并非GC专用。JVM在做很多“VM Operation”时都需要所有线程配合暂停比如类卸载、线程快照做线程dump、偏向锁批量撤销、JVMTI强制终止线程等。这些操作都会走到一个公共机制先让所有线程到达安全点然后执行操作最后恢复线程。所以你在日志里看到的“Stop The World”或“application stopped time”可能不是因为GC而是某个VM Operation。我碰到过一次线上服务周期性卡顿GC日志显示Full GC频率很低但应用停顿时间稳定在几百毫秒。查了打印的VM Operation列表才发现是第三方做某个检测时频繁触发了线程快照操作。这提醒我们碰到停顿先别急着背GC的锅先确认停顿源头。1.3 STW到底停多久谁说了算STW时长取决于多个因素参与停顿的线程数、要枚举的根节点数量、GC算法是否需要处理整个堆、当前安全点同步速度等。其中“根节点枚举”和“安全点同步”就是本文后两章的主角。举个例子堆里有几十GB对象如果每根指针都要靠反射栈帧推断停顿必然爆炸。好在JVM提前用OopMap做了记录让根枚举可以按位图快速回收。而“安全点”决定线程从收到暂停指令到真正挂起需要多久。这两个环节没做好堆再小也可能卡得很明显。2. OopMapJVM怎么知道栈上哪一个是引用2.1 保守式GC的“猜”与准确式GC的“记”GC要扫描根对象首先得知道线程栈上的每个槽位哪些是引用。最简单的想法是把整块栈内存从头到尾扫一遍凡是看起来像引用的64位或者32位数值都当成引用处理。这叫保守式GCConservative GC早期很多虚拟机用这种办法。保守式的麻烦很直观。一个对象的地址可能是0x12345678但某个整型变量恰好也存了这个数字就会被误认成引用导致本应回收的对象被留着。更麻烦的是你没法安全移动对象因为移动后要把栈上的值改掉可你分不清哪些是真实引用哪些是整型万一改了整数程序就错乱了。所以保守式GC往往只能做“标记-清理”不能做复制或压缩。HotSpot后来选择了一条更准的路不靠猜靠记录。JIT编译器在生成机器码时在特定的指令位置生成一份“当前栈帧和寄存器里哪些位置是引用”的映射表这张表就是OopMapOOP就是普通对象指针Ordinary Object Pointer。GC需要用某个栈帧的根时直接查该位置对应的OopMap不用扫描整块栈也不用猜。2.2 OopMap记录了什么在哪里生成OopMap记录的是“在这个指令位置哪些偏移量上的槽位是引用”。典型的做法是用一个位图表示每一位对应一个栈槽或寄存器。比如局部变量表第0位是调用者传进来的对象引用第2位是int第5位是另一个对象字段引用位图里就是第0位为1、第2位为0、第5位为1。这个映射不是每次GC临时去分析字节码而是提前由JIT在编译方法时生成。方法里可能有多个安全点每个安全点对应一份OopMap。当线程执行到某个安全点并挂起后GC就能根据当前PC定位到方法、偏移量进而找到那一份OopMap提取出所有根引用。解释执行时也有对应的机制通常可以通过解释器维护的元数据或类似的栈帧描述得到只是没有JIT代码里那么线程化。我在看JVM源码或相关文档时有个体会OopMap相当于“程序执行位置的静态快照索引”把“所有栈内容”变成了“只关注引用槽位”。没有它准确式GC和对象移动无从谈起。2.3 OopMap为什么是“根枚举加速器”可达性分析的第一步是枚举根节点GC Roots包括线程栈上的局部变量、操作数栈、寄存器里的引用以及静态字段、JNI引用等。线程栈部分最麻烦因为每个线程的栈帧结构都不同而且还在变化。如果JVM没有OopMap就只能扫描全部栈内存再把扫描结果交给解释器做类型判断。这不只是慢还会带来保守式GC的“误伤”问题。有了OopMap线程到达安全点后JVM遍历每个线程当前栈帧顺着OopMap位图直接取到引用槽位几乎没有多余探查。实际影响很直接根枚举最怕“扫栈慢”和“误判多”。OopMap解决的是前者准确式GC解决的是后者。堆越大线程栈越深收益越明显。很多低延迟GC在根枚举阶段依然要借助这些元数据只是把枚举出的根做并发处理把停顿尽量缩小。3. 安全点GC敢动手的精确位置3.1 为什么不能随便喊停就算有了OopMapJVM也不能让线程在任意指令处暂停。原因有两层。第一层OopMap只覆盖了特定位置也就是安全点在非安全点指令处JVM没有为当前PC准备完整的栈帧映射就算停下来了也无法准确枚举你的局部变量。第二层某些指令序列正在跨多个结构操作中间比如一个对象还没写完另一个线程就要垃圾回收看到的对象可能处于非法状态。你可以把线程想象成一个厨师在切菜GC是进来打扫厨房的人。如果厨师切到一半被要求立刻定住他手可能还举着刀台上的菜还没分类打扫的人根本不知道哪些食材该留。必须等到一个“放下刀且台面状态明确”的时刻才能安全接管。安全点就是这些“明确时刻”的集合。当然JVM不需要在每条指令都设置安全点那样性能开销太大。它只需要保证任何可能长时间运行的控制流回边、方法调用、异常跳转处都能被GC截住。换句话说执行块再大也有出口GC只需守在这些出口上。3.2 安全点选在哪选多了浪费选少了卡顿HotSpot里安全点主要选在这样几类位置方法调用指令调用另一个方法前一定有一个清晰的栈帧边界。循环回跳点for、while等循环执行完一轮回到判断条件的那个跳转位置。异常跳转路径抛出异常准备进入异常处理器的位置。特定字节码的边界比如某些会改变栈状态的指令之后。选在哪是精细活。安全点太密意味着每条循环回跳都要做一次安全点检查运行时开销变大安全点太稀线程跑很久都停不下来GC等待时间变长停顿同样不可控。JVM的JIT编译器会结合循环深度、函数调用频率来合理放置安全点同时还要兼顾OopMap可维护性。理解安全点最重要的是区分“安全点检查”和“实际停顿”。检查是廉价的通常一条指令就能完成停顿是昂贵的意味着线程要等待GC结束。3.3 主动式中断与轮询机制老式JVM用过抢占式中断GC开始后强制把所有线程都挂起再检查哪些线程在安全点上不在的让它们继续跑到安全点为止。这个流程粗暴容易误伤而且线程被挂起后恢复路径复杂。现代HotSpot用的是主动式中断。GC准备启动时先设置一个全局的“请求安全点”标志并不直接挂起线程。每个线程在执行到安全点时会主动检查这个标志一旦发现标志被设置就自己阻塞下来等待GC结束。线程在安全点轮询类似等待红绿灯路口安全点必须停下来看一下信号如果红灯亮就排队等待。这个轮询代码不能太重否则每个安全点都做一次昂贵操作应用吞吐量会掉。HotSpot把它实现为一条对特定内存页的读操作test指令非常廉价。检查频率、检查点位置都由JIT在生成代码时决定。3.4 超长循环、JIT优化和安全点之间的恩怨这里有一个经典坑。JIT为了优化性能会把某些循环做“循环不变量外提”“循环展开”等变换。本来循环回跳处都有安全点轮询足够让GC截停但有些优化会把耗时操作挪出循环甚至把循环变成一个大的无安全点的区域导致线程在很长一段时间里不检查安全点标志。GC虽然设置了请求标志却等不到这个线程安全点停顿时间就会飙升。我在调试一个后台任务时遇到过类似场景任务里有个大循环频繁 Full GCGC日志显示“安全点等待线程阻塞时间”异常高。后来发现是某个高版本JIT优化改变了一个热循环的结构升级JDK小版本后不再复现。这种问题的通用排查思路是如果安全点日志显示大量线程阻塞在等待某个“还没到达安全点”的线程优先怀疑是不是有超长循环、JNI调用或者线程在系统调用里长时间不回来。另外提一句JNIJava Native Interface调用是另一个安全点盲区。线程进入JNI方法后如果长时间不回到Java代码GC也只能干等。JVM提供了一些机制比如JNI临界区来降低开销但调优时还是要留意native代码的执行时长。4. 安全区域线程睡觉时怎么办4.1 睡着了到不了安全点怎么破只有运行的线程才会执行到安全点并挂起。如果某个线程因为sleep、wait、锁等待或者干脆被操作系统调走了处于Blocked或Waiting状态它不会执行任何Java字节码永远到不了安全点。这时 GC 会怎么处理总不能为了等它一直卡住。JVM引入了一个概念安全区域Safe Region。它指的是一段代码区间在这段区间里对象的引用关系不会发生变化因此任意时刻暂停都等价于在安全点。典型的例子就是线程阻塞在锁或Object.wait上的时候——它已经不在修改堆了JVM可以安全地把它当作“已经在安全点”。我理解安全区域是安全点的“膨化版”安全点是一个点安全区域是一段连续的“不会破坏GC不变量”的代码。线程状态只要是在安全区域内GC不需要管它是否主动轮询直接认为它处于可挂起状态。4.2 安全区域的进入与离开线程进入安全区域时JVM会记录一个状态离开安全区域前JVM会检查当前是否有GC正在进行。如果有线程不能直接离开要等GC完成后才能继续。这样才能避免线程在GC过程中又跑出去修改堆引用破坏可达性分析的结论。这个“进入-离开”记录是有开销的所以JVM只在真正需要阻塞的场景如锁等待、sleep才进入安全区域。普通运行代码不需要因为正常代码会经过安全点。实际写代码不需要直接跟安全区域打交道它是JVM内部机制。但在排查停顿问题时如果发现“某几个线程长时间停留在某个状态GC等待时间飙升”可以考虑是不是线程长时间停留在安全区域边界之外比如JNI返回前或者反之安全区域退出逻辑在等待GC。5. 实战怎么观察和调优安全点停顿5.1 打开安全点与GC停顿日志平时调优别只盯着GC日志里的“Pause Full GC”还要把安全点统计打开。JDK里常用的开关有-XX:PrintSafepointStatistics打印安全点统计信息。-XX:PrintGCApplicationStoppedTime打印应用被停顿的时间。飞行记录器JFR里的VM Operation事件也可以记录安全点停顿。打开后日志里会有一段类似下面这样的输出不同版本格式略有差异vmop threads_begin threads_end time GC_Collect [ 20 20 0.003 ] RevokeBias [ 20 20 0.001 ]注意看time列如果VM Operation时间很长说明这次停顿本身耗时高如果threads_end与threads_begin差别不大但某个线程没有及时到位说明问题可能出在安全点到达速度上。还有-XX:SafepointTimeout等诊断参数可以设置超时检测一般只在测试环境用生产环境开启需谨慎因为它会增加额外开销。我通常的做法是先在预发环境开一段时间安全点统计把每次停顿按时间切片找出哪些VM Operation最耗时再决定优化方向。线上最好不要常开详细安全点统计会影响性能用抽样或者JFR短时间采集就够了。5.2 常见“安全点慢”排查套路几类高频问题堆扫描慢根枚举本身耗时高多和OopMap生成不充分、栈帧深、线程多有关。线程到达安全点慢热循环没有及时轮询或者JNI调用时间过长。安全点同步开销大线程数量极多时所有线程同时被勾住再释放调度开销不可忽略。VM Operation本身耗时高比如ThreadDump、类卸载并不完全是安全点机制的问题。排查时先把停顿时间拆成“同步时间”和“VM操作时间”。同步时间长了优先找线程在等什么VM操作时间长了再去分析GC算法、堆大小或操作本身。比如我遇到过某个服务设置了超大线程池几百个线程用于IO等待结果每次GC同步时间比实际GC标记时间还长。后来用ThreadLocal Handshake这类新机制或者分组同步同步开销才下来。如果你在用老JDK又不方便升级降低参与同步的线程数也是一个方向但要谨慎评估业务影响。5.3 低延迟收集器是怎么绕开安全点痛的ZGC、Shenandoah这类低延迟收集器能大幅压缩停顿关键思路是“尽量把GC工作搬到与应用并发执行”同时把必须暂停的部分做得更短。它们同样要枚举根节点、处理安全点但通过并发根扫描、分代化等设计让应用线程在很多阶段不用全部停住。例如部分收集器支持线程局部握手Thread Local Handshake让每个线程单独尽快响应而不是强制所有线程同时到达一个全局安全点从而减少同步风暴。结合前面讲的OopMap你可以理解无论GC怎么演进根枚举还是需要准确的栈映射信息安全点还是那个用来让线程稳定下来的机制。只是新一代算法把需要“全局停”的次数和范围缩小了把STW从“全世界的暂停”变成“极短时间的世界快照”。我个人的体会是学JVM内存管理不用一上来钻底层C实现。先把三个概念串成一条线STW是结果安全点是时机OopMap是工具。后面再去看ZGC的并发处理、看G1的Region划分、甚至看Android的ART GC都会顺畅很多。建议你找一个简单的Demo开上安全点日志触发几次GC亲眼看一看线程数和停顿时间列比看十篇文章都有用。
RELATED

相关推荐

QT之FTP上传下载工具源码解析:选型、实现与避坑指南

QT之FTP上传下载工具源码解析:选型、实现与避坑指南

简介:这是一份基于QT5框架的FTP客户端功能工具源码,面向需要在Windows、Linux及嵌入式Linux等平台实现FTP文件上传与下载的C开发者。项目通过QNetworkAccessManager与QFtp类的配合,封装了FTP服务器连接、用户登录、目录浏览、文件传输等核心流…

📅 2026/10/15 12:41:20
Loop Engineering:多智能体协作的工程范式

Loop Engineering:多智能体协作的工程范式

1. 什么是Loop Engineering:不是“循环”,而是智能体协作的底层操作系统 Loop Engineering这个词,最近在技术圈里突然密集出现,但很多人第一反应是——这不就是个for循环?或者跟控制理论里的反馈回路有关?其…

📅 2026/10/15 12:36:11
五种图像生成范式本质解析:扩散、自回归、视觉token化、概念具象化与端侧残差

五种图像生成范式本质解析:扩散、自回归、视觉token化、概念具象化与端侧残差

1. 这不是“又一个文生图模型”——而是五种截然不同的图像生成范式正在交汇你点开这篇内容,大概率是因为在ComfyUI节点里看到FLUX.2、Seedream、Z-image、Qwen-Image、GLM-Image这五个名字并列出现,却完全搞不清它们到底在干什么。有人在蓝奏云下载Z-im…

📅 2026/10/15 12:36:11
MORE NEWS

更多资讯

📰

等保测评安全运维管理:环境、资产与流程要点解析

接触过等保测评的运维负责人大概都有同感:技术部分可以通过部署设备、调整策略快速见效,但到了安全运维管理这一模块,准备起来总觉得使不上力。它不单纯检查手上有几份文档,而是把制度建设、人员行为、现场记录串起来综合判断。可…

📰

仓颉编程语言 std.time 跨时区 DateTime 比较实战:以 UTC 时刻为基准的相等与大小判定

文档知识库教程OpenHarmony 【免费下载链接】CangjieCorpus 本仓库为仓颉编程语言语料库,聚焦 仓颉编程语言 核心知识体系,整合官方开发指南、API 文档、典型代码示例及语法规范等多维度资源,经结构化处理与专业标注,构建为支持 R…

📰

压缩包密码破解别只靠 CPU:GPU 并行加速与 hashcat 实战指南

简介:不少用户都遇到过rar或zip压缩包密码遗忘的尴尬,这套基于GPU加速的密码破解工具包正是为此设计,面向普通使用者、IT运维及安全测试人员,无需手动搭建复杂环境,按说明操作两步即可尝试找回口令。资源共2000个文件&…

📰

Ubuntu 22.04 CH340驱动排查与ttyUSB固定设备名指南

简介:面向Ubuntu 22.04用户,这份资源专注于解决CH340 USB转串口芯片在Linux内核中未被默认支持、需要手动编译安装驱动的问题。适用于嵌入式开发、单片机调试、物联网设备通信等场景,尤其适合刚接触Linux驱动安装的初学者。包体包含16个文件&…

📰

Redis面试常问2-- 从海量数据里查询某一固定前缀的key? SCAN cursor 实战避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Natron Backdrop 节点完全指南:用分组容器组织节点图的实战与源码解析

音视频视频处理图形学桌面应用 【免费下载链接】Natron Open-source video compositing software. Node-graph based. Similar in functionalities to Adobe After Effects and Nuke by The Foundry. 项目地址: https://gitcode.com/gh_mirrors/na/Natron 点击查看 …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬