jvm之【垃圾回收器】
目录
- 一、整体分类逻辑
- 二、逐个解析核心收集器
- 1. Serial 收集器(新生代)
- 2. Serial Old 收集器(老年代)
- 3. ParNew 收集器(新生代)
- 4. Parallel Scavenge 收集器(新生代)
- 5. Parallel Old 收集器(老年代)
- 7. G1 收集器(全代 / 老年代)
- 总结
一、整体分类逻辑
按适用代(新生代 / 老年代) 和 并发特性 划分,覆盖不同场景需求(吞吐量优先、低延迟优先等):
Serial | 新生代 | 单线程、STW(暂停所有用户线程,应用程序暂停)、复制算法 | 客户端应用(如桌面程序) |
---|---|---|---|
Serial Old | 老年代 | 单线程、STW(暂停所有用户线程)、标记 - 整理算法 | 配合 Serial 新生代 |
ParNew | 新生代 | 多线程、STW(暂停所有用户线程)、复制算法 | 配合 CMS(老年代) |
Parallel Scavenge | 新生代 | 多线程、吞吐量优先(用最短的gc时间完成回收,把cpu资源让给用户线程)、复制算法 | 后台计算(批处理) |
Parallel Old | 老年代 | 多线程、吞吐量优先、标记 - 整理 | 配合 Parallel Scavenge |
CMS | 老年代 | 并发收集(低延迟)、标记 - 清除 | 服务端低延迟需求 |
收集器 | 适用代 | 核心特点 | 典型场景 |
G1 | 全堆(替代分代) | 分区回收、可预测停顿、标记 - 整理 | 大型服务端应用 |
吞吐量优先的垃圾收集器和cms和G1:
减少停顿影响:都在一定程度上致力于减少垃圾回收对应用程序造成的停顿影响。虽然吞吐量优先收集器在收集过程中也会发生 STW(Stop The World,暂停所有用户线程),但它们通过多线程等方式尽量缩短这个时间,以提升吞吐量;CMS 采用并发收集的方式,尽量让垃圾回收线程和应用程序线程同时运行,减少对应用程序的中断;G1 可以预测停顿时间,通过合理规划回收区域,将停顿控制在可接受的范围内,提升应用程序的响应性和稳定性。
吞吐量优先的不是并发收集:
你说得对,吞吐量优先的收集器(像 Parallel Scavenge、Parallel Old)不是并发收集,它和 CMS、G1 等并发收集器有明显区别,主要差异体现在以下方面:
收集时与用户线程的关系:
-
吞吐量优先收集器:在进行垃圾收集时,会触发“Stop The World(STW)”,也就是暂停所有的用户线程,只有垃圾收集线程在工作。例如 Parallel Scavenge 收集新生代、Parallel Old 收集老年代时,用户程序会暂时停止运行,等垃圾收集完成后,用户线程才会恢复执行。
-
CMS、G1(并发收集相关):
-
CMS(Concurrent Mark Sweep):它的“并发标记”和“并发清除”阶段,能够与用户线程同时运行,只有在初始标记和重新标记阶段会有短暂的 STW,大大减少了垃圾收集对用户程序的停顿影响,更注重低延迟。
-
G1(Garbage - First):在大部分垃圾收集阶段,也能和用户线程并发执行,而且通过分区回收等机制,可预测停顿时间,兼顾吞吐量和低延迟。
设计目标:
-
吞吐量优先收集器:核心目标是提高“吞吐量”,也就是让应用程序线程有效执行任务的时间占总时间的比例尽可能高。它会尽可能缩短垃圾收集所占用的总时间,哪怕偶尔有较长时间的 STW,只要整体上能让应用程序更多时间用于业务处理即可。
-
CMS、G1:更注重“低延迟”,希望垃圾收集过程对用户程序的停顿时间尽可能短,让应用程序能更及时地响应用户请求,尤其适合服务端等对响应速度要求高的场景。
二、逐个解析核心收集器
1. Serial 收集器(新生代)
- 核心逻辑:
单线程执行垃圾回收(STW:Stop The World,暂停所有用户线程),用复制算法(新生代→Eden + Survivor 区回收)。 - 特点:
实现简单、无线程切换开销,适合客户端应用(如桌面程序、小内存场景),但大内存下 STW 时间长。
2. Serial Old 收集器(老年代)
-
核心逻辑:
单线程执行,用标记 - 整理算法(老年代对象存活率高,整理后无 碎片)。 -
特点:
常作为 CMS 回退方案(CMS 并发失败时,改用 Serial Old 应急),或配合 Serial 新生代使用(客户端场景)。
3. ParNew 收集器(新生代)
-
核心逻辑:
Serial 的多线程版本(多线程 STW,复制算法),本质是 “并行版 Serial”。 -
特点:
唯一能配合 CMS 老年代收集器 的新生代收集器(CMS 自身不处理新生代),适合服务端低延迟场景(需与 CMS 搭配)。4. Parallel Scavenge 收集器(新生代)
-
核心逻辑:
多线程 STW,用复制算法,以 “吞吐量优先” 为目标(吞吐量 = 运行用户代码时间 / (运行用户代码 + 垃圾回收时间))。 -
特点:
提供-XX:GCTimeRatio
(控制 GC 时间占比)、-XX:MaxGCPauseMillis
(目标停顿时间,尽力而为)等参数,适合后台批处理任务(如大数据计算)。
5. Parallel Old 收集器(老年代)
-
核心逻辑:
Parallel Scavenge 的老年代版本,多线程 STW,用标记 - 整理算法,同样以 “吞吐量优先” 为目标。 -
特点:
与 Parallel Scavenge 搭配,形成 “吞吐量优先” 的全代收集方案(新生代 Parallel Scavenge + 老年代 Parallel Old),适合纯后台任务。两者的核心是追求高吞吐量(用户代码运行时间占比更高),而非 “低延迟”。为了提高吞吐量,它们会尽量减少 GC 的频率(让内存用得更满再回收),但单次 GC 的停顿时间可能较长。
- 多线程并行收集,缩短单次 GC 时间
-
Parallel GC 的核心是“并行”:新生代和老年代的 GC 过程都由多个线程同时执行(而非单线程),能显著缩短单次 STW 的持续时间。
-
例如,同样回收 1GB 内存,单线程可能需要 100ms,而 4 线程并行可能只需 30ms——单次停顿时间更短,意味着 GC 占用的总时间更少,留给用户线程的时间自然更多。
- 自适应调节策略,减少 GC 触发频率
-
Parallel GC 内置了“吞吐量优先”的自适应机制:会根据应用的运行情况(如对象创建速率、内存占用)自动调整新生代大小、晋升年龄等参数,尽量让 GC 触发的频率降低。
-
例如,通过增大新生代空间,减少 Minor GC 的次数;优化老年代回收时机,避免频繁 Full GC——GC 次数少了,总停顿时间累加自然更少,用户线程的连续执行时间更长。
- 专注吞吐量优化,不做额外开销操作
-
与 CMS 不同,Parallel GC 不追求“并发执行”(避免了并发阶段对 CPU 资源的争抢),也没有写屏障等额外开销(这些会消耗 CPU 资源,间接占用用户线程的执行时间)。
-
它的设计目标单一:用最短的 GC 时间完成回收,把更多 CPU 资源让给用户线程——虽然有 STW,但 GC 本身的“时间成本”最低,因此用户线程的有效执行时间占比更高。
- CMS 收集器(老年代)
-
核心逻辑:以 **“低延迟” 为目标 **,用
标记 - 清除算法
(老年代),分 4 阶段:
-
初始标记(STW):标记 GC Roots 直接关联的对象(快)。
-
并发标记:并发遍历对象图(无 STW(不停顿),用户线程继续运行)。
-
重新标记(STW):修正并发标记的遗漏(如 finalize 方法、线程引用变化)。
停顿时间短的原因是产生变动对象数量不多
-
并发清除:并发回收未标记对象(无 STW)。
-
-
特点:
首次实现 “并发回收”(用户线程与 GC 线程部分并行),适合服务端低延迟场景如 Web 服务器)。但有缺陷:
- 内存碎片(标记 - 清除算法)→ 老年代可能无法分配大对象,触发 Full GC。
- 并发开销(线程竞争)、浮动垃圾(并发清除阶段时新产生的垃圾无法回收)。
7. G1 收集器(全代 / 老年代)
-
核心逻辑:
打破 “分代” 边界,将堆划分为 2048 个左右的 Region(每个 Region 可动态属于新生代 / 老年代),用标记 - 整理算法,目标是 “可预测的停顿时间”。 -
关键流程(简化):
- 初始标记(STW):标记 GC Roots 关联对象。
- 并发标记:遍历对象图,计算 Region 的 “垃圾占比”。
- 最终标记(STW):修正并发标记结果。
- 筛选回收(STW):优先回收垃圾占比高的 Region(控制停顿时间)。
-
特点:
- 适合大内存服务端(如 8G+ 堆),可通过
-XX:MaxGCPauseMillis
控制停顿。 - 替代 CMS 成为现代 JVM 首选(如 JDK 9+ 默认 G1),解决 CMS 碎片、浮动垃圾等问题。
总结
- 适合大内存服务端(如 8G+ 堆),可通过
这些收集器是 JVM 垃圾回收的 “工具库”,核心差异体现在:
- 代分工:新生代 / 老年代专用,或跨代(G1)。
- 目标侧重:吞吐量优先(Parallel 系列)、低延迟(CMS、G1)、简单场景(Serial 系列)。
- 并发能力:单线程(Serial)、多线程并行(ParNew、Parallel)、并发(CMS、G1)。
实际应用中,需根据业务场景(如服务端低延迟 vs 后台批处理)选择搭配,G1 因 “可预测停顿” 和 “大内存适配”,逐渐成为主流替代方案。