JVM垃圾回收全攻略:标记清除、复制、整理及新一代收集器
JVM垃圾回收全攻略:标记清除、复制、整理及新一代收集器
JVM垃圾回收机制是Java开发中一个非常重要且复杂的主题。本文将全面介绍JVM垃圾回收的基本原理、常见算法、分代收集策略以及现代垃圾收集器的实现方式,帮助开发者深入理解JVM内存管理机制。
JVM垃圾回收全攻略:标记清除、复制、整理及新一代收集器
一、垃圾回收的基本思想
JVM 的垃圾回收(Garbage Collection, GC)主要解决内存管理问题,自动回收不再被引用的对象,避免内存泄漏,同时降低开发者的内存管理负担。核心思想基于可达性分析,从 GC Roots(如栈中局部变量、静态属性、常驻线程等)出发,判定哪些对象是“活”的,其余均可回收。
二、常见的垃圾回收算法
1. 引用计数算法(Reference Counting)
原理:
每个对象维护一个引用计数器,当计数为 0 时说明对象不再被使用,从而回收内存。缺陷:
不能解决循环引用问题(例如两个对象互相引用但外部无引用)。
维护计数带来额外的性能开销。
现状:
现代 JVM 几乎不采用引用计数,而是使用可达性分析。
2. 可达性分析(Reachability Analysis)
原理:
从一系列被称为 GC Roots 的根节点出发,通过递归或迭代遍历整个对象图,标记出所有可达对象,未被标记的对象即为垃圾。优点:
能正确处理循环引用问题。
不需要维护额外的计数信息。
应用:
作为大部分垃圾收集器的基础技术。
3. 标记-清除(Mark-Sweep)算法
- 过程:
- 标记阶段:遍历所有可达对象并打上标记。
- 清除阶段:扫描整个堆,回收未被标记的对象。
缺陷:
产生大量内存碎片,可能导致后续分配失败。
清除阶段需要遍历整个堆,停顿时间较长。
改进:
随后出现了标记-整理算法,解决内存碎片问题。
4. 标记-整理(Mark-Compact)算法
- 过程:
- 标记阶段:与标记-清除相似。
- 整理阶段:将存活对象移动到堆的一端,形成连续的空闲内存区域。
优点:
避免了内存碎片问题,便于大对象的分配。
缺陷:
移动对象需要更新引用,带来额外开销,可能引起较长停顿。
5. 复制算法(Copying)
过程:
将内存划分为两个区域(From 区和 To 区),只使用其中一块。当 GC 触发时,将存活对象复制到另一块区域,然后一次性清空当前区域。优点:
无碎片问题,复制过程简单高效。
缺陷:
需要额外 50% 的内存空间,适合对象存活率低的区域。
应用:
主要用于新生代垃圾回收,因为新创建的对象存活率低,复制开销较小。
6. 分代收集策略(Generational Collection)
JVM 根据对象存活周期的不同,将堆划分为不同的区域,分别采用适合的垃圾收集算法:
(1)新生代(Young Generation)
特点:
大部分对象生命周期短暂。
使用复制算法(Eden 区与两个 Survivor 区交替使用)。
过程:
对象首先分配在 Eden 区,经过几次 Minor GC 后,存活对象在 Survivor 区之间复制;达到一定年龄后晋升到老年代。
(2)老年代(Old Generation)
特点:
存放生命周期较长的对象。
使用标记-整理或标记-清除算法。
注意:
因为老年代对象较多,Full GC 时可能会有较长停顿。
(3)元空间/永久代(Metaspace/PermGen)
功能:
存放类信息、常量池、方法区数据。
演进:
JDK 8 之前使用永久代(PermGen),JDK 8 之后改为元空间(Metaspace),避免了固定大小的问题。
7. 现代垃圾收集器
(1)CMS(Concurrent Mark-Sweep)
特点:
采用并发标记和清除,尽量缩短 STW(Stop The World)时间。
不整理内存,可能会导致碎片化。
适用场景:
需要低停顿时间的应用,但对内存碎片较为宽容。
(2)G1(Garbage First)
特点:
将堆划分为多个大小相等的区域(Region),不再区分新生代和老年代的固定空间。
采用混合回收策略,根据区域回收收益排序,优先回收垃圾最多的区域,从而达到低停顿与高吞吐量的平衡。
优势:
更好地控制 GC 停顿时间,适用于大内存场景。
内部机制:
包括初始标记、并发标记、重新标记和最终清除阶段,部分阶段仍是 STW,但时间可控。
(3)低延迟收集器:ZGC 与 Shenandoah
ZGC:
适用于超大堆内存(TB 级别),目标是将停顿时间控制在毫秒级别。
Shenandoah:
与 ZGC 类似,侧重于并发压缩和低停顿设计。
特点:
通过并发标记、并发整理和多种优化技术,实现低延迟和高吞吐量的平衡。
三、总结
基本原理:
现代 JVM 垃圾回收的核心是从 GC Roots 开始的可达性分析,借助多种算法(标记-清除、复制、标记-整理等)来识别并回收垃圾对象。分代收集:
根据对象生命周期的特点,将堆分为新生代和老年代,新生代常采用复制算法,而老年代则使用标记-整理或标记-清除,以优化性能和降低停顿。现代垃圾收集器:
- CMS 和 G1通过并发和区域划分等技术降低 GC 停顿,适应大规模和高吞吐量场景;
- ZGC 和 Shenandoah则专注于极低停顿,适合对延迟敏感的应用场景。