垃圾回收算法与收集器对比 垃圾回收(GC)是 JVM 自动管理内存的核心机制。理解各种 GC 算法和收集器,是 JVM 调优的基础。
判断对象是否存活 引用计数法 对象A (ref_count=2) <-- 对象B引用 <-- 对象C引用
缺点 :无法解决循环引用问题。
可达性分析(JVM采用) GC Roots ├── 虚拟机栈中的本地变量 ├── 方法区中静态属性引用的对象 ├── 方法区中常量引用的对象 ├── 本地方法栈中JNI引用的对象 └── 所有被同步锁持有的对象 从GC Roots开始向下搜索,不可达的对象可被回收
垃圾回收算法 1. 标记-清除(Mark-Sweep) 阶段1:标记所有存活对象 ┌─────────────────┐ │ ██ ░░ ██ ░░ │ ██=存活 ░░=垃圾 └─────────────────┘ 阶段2:清除垃圾对象 ┌─────────────────┐ │ ██ ██ │ 产生内存碎片 └─────────────────┘
优点 :简单缺点 :产生内存碎片,分配大对象时可能失败
2. 复制(Copying) 阶段1:将存活对象复制到另一块内存 From空间 To空间 ┌──────────┐ ┌──────────┐ │ ██ ░░ │ --> │ ██ ██ │ │ ██ ░░ │ │ │ └──────────┘ └──────────┘ 阶段2:清空From空间,交换角色
优点 :无碎片,效率高缺点 :内存利用率只有 50%
3. 标记-整理(Mark-Compact) 阶段1:标记存活对象 ┌─────────────────┐ │ ██ ░░ ██ ░░ │ └─────────────────┘ 阶段2:将存活对象向一端移动 ┌─────────────────┐ │ ██ ██ │ 无碎片 └─────────────────┘
优点 :无碎片,内存利用率高缺点 :移动对象需要更新引用,停顿时间较长
垃圾收集器 1. Serial 收集器 单线程: ┌────────┬────────┬────────┐ │ 工作 │ GC │ 工作 │ └────────┴────────┴────────┘ Stop The World
新生代 :复制算法
老年代 :Serial Old(标记-整理)
特点 :单线程,简单高效
适用 :客户端模式、内存小的场景
2. ParNew 收集器 Serial 的多线程版本,唯一能与 CMS 配合的新生代收集器。
-XX:+UseParNewGC -XX:ParallelGCThreads=4
3. Parallel Scavenge 收集器 目标 :达到可控的吞吐量。
吞吐量 = 运行用户代码时间 / (运行用户代码时间 + GC时间)
-XX:+UseParallelGC -XX:MaxGCPauseMillis=200 -XX:GCTimeRatio=99 -XX:+UseAdaptiveSizePolicy
适用 :后台计算型任务。
4. CMS 收集器(Concurrent Mark Sweep) 目标 :最短停顿时间。
阶段1:初始标记(STW,很快) 阶段2:并发标记(与用户线程并发) 阶段3:重新标记(STW,比初始标记长) 阶段4:并发清除(与用户线程并发) ┌────┬──────────────────┬────┬──────────────────┐ │STW │ 并发标记 │STW│ 并发清除 │ └────┴──────────────────┴────┴──────────────────┘
优点 :低停顿缺点 :
内存碎片(Mark-Sweep)
对 CPU 资源敏感
无法处理浮动垃圾(Concurrent Mode Failure)
-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSCompactAtFullCollection
5. G1 收集器(Garbage First) JDK 9 后的默认收集器。
设计思想 ┌────┬────┬────┬────┬────┬────┬────┬────┐ │ E │ S │ O │ H │ O │ E │ S │ O │ Region(1~32MB) └────┴────┴────┴────┴────┴────┴────┴────┘ E=Eden, S=Survivor, O=Old, H=Humongous(大对象)
将堆划分为多个 Region
优先回收垃圾最多的 Region(Garbage First)
回收过程 Young GC: ┌────┬────┬────┬────┬────┬────┬────┬────┐ │ E │ E │ E │ │ │ │ │ │ 回收Eden Region └────┴────┴────┴────┴────┴────┴────┴────┘ ↓ ┌────┬────┬────┬────┬────┬────┬────┬────┐ │ S │ │ │ O │ │ │ │ │ 存活对象->Survivor/Old └────┴────┴────┴────┴────┴────┴────┴────┘ Mixed GC(并发标记后): 回收年轻代 + 部分老年代Region
参数 -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=16m -XX:InitiatingHeapOccupancyPercent=45
6. ZGC / Shenandoah(低延迟) JDK 11+(ZGC)和 JDK 12+(Shenandoah)。
目标 :TB 级堆内存下停顿时间 < 10ms。
-XX:+UseZGC -XX:+UseShenandoahGC
收集器对比
收集器
算法
目标
适用场景
Serial
复制/整理
简单高效
单核、小内存
ParNew
复制
降低停顿
配合CMS
Parallel Scavenge
复制/整理
高吞吐量
后台计算
CMS
标记-清除
低停顿
Web应用
G1
复制/整理
平衡
大堆内存
ZGC
复制
超低停顿
大堆、低延迟
组合关系 新生代 老年代 ───────────────────────────── Serial + Serial Old ParNew + CMS Parallel Scavenge + Parallel Old G1(同时管理新生代和老年代) ZGC(同时管理)
选择建议
场景
推荐
单核/小内存(<100MB)
Serial
多核、追求吞吐量
Parallel Scavenge
Web应用、追求低延迟
CMS / G1
大堆内存(>6G)
G1
超大堆、极低延迟要求
ZGC
JDK 9+
G1(默认)
总结
算法
优点
缺点
标记-清除
简单
碎片
复制
高效无碎片
空间减半
标记-整理
无碎片
移动对象开销
没有最好的收集器,只有最合适的收集器。根据应用特点(延迟敏感 vs 吞吐量优先)和 JDK 版本选择合适的收集器。