每日面试题13:垃圾回收器什么时候STW?

STW是什么?------深入理解JVM垃圾回收中的"Stop-The-World"

在Java程序运行过程中,JVM会通过垃圾回收(GC)自动管理内存,释放不再使用的对象以腾出空间。但你是否遇到过程序突然卡顿的情况?这可能与GC过程中的​​Stop-The-World(STW,全局停顿)​​有关。本文将围绕"GC何时STW"展开,重点解析CMS与G1回收器的STW机制,并结合三色标记法说明其必要性。


一、STW的本质与核心作用

​Stop-The-World(STW)​ ​ 是JVM在垃圾回收过程中,为保证内存回收的​​正确性​ ​和​​一致性​​,临时暂停所有应用线程(User Thread)执行的现象。简单来说,就是"世界停止了",只有GC线程在工作。

为什么需要STW?

垃圾回收的核心是​​准确区分存活对象与可回收对象​​。若应用线程在GC过程中继续运行,可能导致:

  • ​对象状态变更​:应用线程可能修改对象的引用关系(如创建新对象、销毁旧对象、修改指针指向),导致GC标记结果失效;
  • ​数据不一致​:若GC线程与应用线程同时操作同一块内存,可能引发竞态条件(Race Condition),破坏内存管理的准确性。

因此,STW是GC保证自身逻辑正确的"保护机制",但过长或频繁的STW会显著降低程序性能(尤其是对延迟敏感的应用)。


二、三色标记法:GC标记对象的"通用语言"

无论是CMS还是G1,GC标记阶段均基于​​三色标记法​​(Tri-Color Marking)实现。这是一种通过颜色标记对象存活状态的并发标记算法,三种颜色含义如下:

颜色 含义
白色 未被GC线程访问过的对象,默认视为"可回收垃圾"(未被标记)。
灰色 已被GC线程访问过,但其​​引用的其他对象​​尚未处理(待遍历的"边界"对象)。
黑色 已被GC线程完整处理(自身标记为存活,且所有引用对象也已处理),确认"存活"。
标记过程的关键:

GC线程从GC Roots(如栈帧局部变量、静态变量、JNI引用等)出发,将直接关联的对象标记为灰色(初始阶段);随后递归处理灰色对象的引用,将其目标对象标记为灰色,自身升级为黑色(并发阶段)。最终未被标记为黑色的白色对象将被回收。

但三色标记法存在一个天然缺陷:​​若应用线程在标记过程中修改了对象的引用关系(如删除灰色对象到白色对象的引用),可能导致白色对象被错误回收(漏标)​​。因此,GC需要通过STW阶段修正这些变动。


三、CMS回收器的STW阶段解析

CMS(Concurrent Mark-Sweep,并发标记-清除)是早期的并发GC算法,目标是​​减少STW时间​ ​,适用于对延迟敏感的场景。其核心流程包含4个阶段,其中​​初始标记​ ​和​​重新标记​​需要STW,其余阶段与应用线程并发执行。

1. 初始标记(Initial Mark,STW)
  • ​目标​:快速标记GC Roots直接关联的对象(即从GC Roots出发的第一层可达对象)。
  • ​STW原因​:需暂停所有应用线程,确保标记的准确性(避免应用线程在此时修改GC Roots的引用关系)。
  • ​耗时​:非常短暂(通常仅毫秒级),因为仅标记直接关联对象。
2. 并发标记(Concurrent Mark,并发)
  • ​目标​:从初始标记的灰色对象出发,递归遍历所有可达对象,将其标记为黑色(存活)。
  • ​STW状态​:与应用线程并发执行(不暂停)。
  • ​风险​:若应用线程在并发标记期间修改了对象的引用关系(如删除灰色对象到白色对象的引用),可能导致部分存活对象被漏标为白色。
3. 重新标记(Remark,STW)
  • ​目标​:修正并发标记阶段因应用线程运行导致的标记变动(如漏标、错标)。
  • ​STW原因​:需暂停应用线程,确保所有标记变动被正确处理(例如,通过"增量更新"算法记录并发期间的引用变更,重新扫描这些变更的对象)。
  • ​耗时​:比初始标记长,但远短于完全串行的标记过程(通常为初始标记的数倍)。
4. 并发清除(Concurrent Sweep,并发)
  • ​目标​:回收未被标记的白色对象(垃圾),释放内存空间。
  • ​STW状态​:与应用线程并发执行(不暂停)。
  • ​特点​:CMS采用"标记-清除"算法,因此不会移动存活对象,可能导致内存碎片(长期运行后可能引发Full GC)。
CMS的STW总结:

CMS通过并发标记和清除大幅减少了STW时间,但初始标记和重新标记仍需短暂停顿。其STW总耗时通常在10ms~100ms级别(取决于堆大小和对象复杂度)。


四、G1回收器的STW阶段解析

G1(Garbage-First)是JDK9及以后默认的GC算法,设计目标是​​平衡吞吐量与延迟​​(支持设置最大停顿时间)。与CMS不同,G1将堆划分为多个独立的Region(默认2MB~32MB),并通过"标记-整理"思想优化内存布局。其核心流程同样包含4个阶段,但STW的触发逻辑与CMS有显著差异。

1. 初始标记(Initial Mark,STW)
  • ​目标​:标记GC Roots直接关联的对象,并记录每个Region中"已存活对象"的数量(用于后续回收价值排序)。
  • ​STW状态​:与应用线程并发执行(仅标记GC Roots直接关联的对象,耗时极短)。
  • ​特点​:G1的初始标记通常与Minor GC(年轻代GC)合并执行,进一步减少STW时间。
2. 并发标记(Concurrent Mark,并发)
  • ​目标​:递归标记所有可达对象,统计每个Region的存活对象比例。
  • ​STW状态​:与应用线程并发执行(不暂停)。
  • ​优化​:G1通过"SATB(Snapshot-At-The-Beginning)"算法记录初始标记时的对象快照,即使后续应用线程修改引用关系,也能通过对比快照修正标记(减少漏标)。
3. 最终标记(Final Mark,STW)
  • ​目标​:处理并发标记阶段SATB快照中未被处理的变动(如新增的白色对象),确保标记结果最终准确。
  • ​STW状态​:与应用线程短暂并发(仅扫描SATB队列中的变更,耗时通常在1ms~5ms)。
4. 筛选回收(Live Data Counting and Evacuation,STW)
  • ​目标​:根据各Region的存活对象比例和回收价值(存活对象越少、Region越小,回收价值越高),选择部分Region进行回收,并将存活对象移动到其他Region(整理内存)。
  • ​STW原因​:需暂停应用线程,确保存活对象移动过程的原子性(避免应用线程访问正在移动的对象)。
  • ​耗时​:取决于需要回收的Region数量(若仅回收少量Region,STW可控制在10ms以内)。
G1的STW总结:

G1的STW主要集中在初始标记、最终标记和筛选回收阶段,但通过并发标记和SATB算法大幅缩短了单次STW的时间。由于G1支持"增量回收"(每次只回收部分Region),其平均STW时间可控制在用户设定的阈值内(如不超过200ms)。


五、CMS与G1的STW对比

维度 CMS G1
​STW阶段​ 初始标记、重新标记 初始标记、最终标记、筛选回收
​单次STW时长​ 较长(重新标记可能达百毫秒级) 更短(筛选回收可通过Region选择优化)
​内存碎片​ 标记-清除算法导致碎片积累 标记-整理算法避免碎片
​适用场景​ 老年代小堆(<8GB),对延迟敏感 大堆(>8GB),需平衡吞吐量与延迟

总结

STW是GC保证正确性的必要代价,但通过优化标记算法(如三色标记+SATB)和并发设计(如CMS的并发标记、G1的Region划分),现代GC已将STW时间控制在可接受范围内。理解不同回收器的STW阶段,有助于我们在实际开发中根据业务需求(如延迟敏感型或吞吐量优先型)选择合适的GC算法,并通过JVM参数调优(如-XX:+UseConcMarkSweepGC-XX:+UseG1GC)进一步降低停顿时间。

相关推荐
悟能不能悟9 分钟前
在 IntelliJ IDEA 中打开这个用于设置 Git 用户名(Name)和邮箱(Email)的特定弹窗
java·git·intellij-idea
少许极端19 分钟前
数据结构3-单双链表的泛型实现及ArrayList与LinkedList的区别
java·数据结构·linkedlist·顺序表与链表区别
lang2015092831 分钟前
Apache Ignite 中事务的使用方式和机制
java·apache·ignite
丶小鱼丶40 分钟前
Spring之【Bean后置处理器】
java·spring
小比卡丘42 分钟前
【C++进阶】第7课—红黑树
java·开发语言·c++
1.01^10001 小时前
[1-01-01]第42节:不可变字符序列 - String类中常用的API
java
程序员瓜叔1 小时前
JAVA知识点(四):SpringBoot与分布式、微服务架构
java·spring boot·架构
超浪的晨1 小时前
Java 单元测试详解:从入门到实战,彻底掌握 JUnit 5 + Mockito + Spring Boot 测试技巧
java·开发语言·后端·学习·单元测试·个人开发
中东大鹅1 小时前
SpringBoot创建项目的方式
java·spring boot·github
泰勒疯狂展开1 小时前
Java研学-RabbitMQ(三)
java·rabbitmq·java-rabbitmq