一、为什么需要垃圾收集?
在Java应用中,98%的对象都是“朝生夕死”的。如果没有自动垃圾收集(Garbage Collection, GC),开发者手动管理内存会导致:
-
内存泄漏风险增加(C/C++开发者深有体会)
-
代码复杂度呈指数级上升
-
系统稳定性难以保障
二、垃圾收集核心算法剖析
1. 标记-清除(Mark-Sweep)
// 伪代码示例 void markSweep() { markPhase(); // 从GC Roots遍历标记存活对象 sweepPhase(); // 清除未标记对象 }
特点:
-
内存碎片化问题严重
-
执行效率随堆大小线性下降
-
CMS收集器的老年代回收采用此算法
2. 复制算法(Copying)
优势:
-
无内存碎片
-
分配内存只需移动指针
-
缺点:内存利用率仅50%
-
应用于新生代的Survivor区
3. 标记-整理(Mark-Compact)
void markCompact() { markPhase(); compactPhase(); // 滑动整理对象 }
适用场景:
-
老年代回收(Parallel Old、Serial Old)
-
避免碎片的同时保证内存连续性
三、JVM经典垃圾收集器对比
收集器 | 工作线程 | 算法组合 | 最大停顿时间 | 适用场景 |
---|---|---|---|---|
Serial | 单线程 | 复制+标记-整理 | 100ms+ | 客户端模式 |
Parallel Scavenge | 多线程 | 复制+标记-整理 | 10-100ms | 吞吐量优先 |
CMS | 并发 | 标记-清除 | 10-50ms | 低延迟要求 |
G1 | 并发 | 分区+复制+标记-整理 | 10ms级 | 大堆内存 |
ZGC | 并发 | 染色指针+读屏障 | <1ms | 超大堆(TB级) |
各收集器关键参数示例
Parallel Scavenge/Parallel Old
-XX:+UseParallelGC -XX:MaxGCPauseMillis=100 # 目标停顿时间 -XX:GCTimeRatio=99 # 吞吐量目标(GC时间占比1%)
CMS
-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 # 老年代70%时触发
G1
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=32m
四、现代垃圾收集技术突破
1. ZGC的核心创新
-
染色指针(Colored Pointers)
-
内存多重映射(Multi-Mapping)
-
支持16TB堆内存,停顿<1ms
2. Shenandoah的亮点
-
并发的对象整理
-
与应用程序线程完全并发
-
突破性的Brooks Pointer设计
五、调优实战技巧
内存泄漏排查四步法
-
jmap -histo:live <pid>
查看对象分布 -
jmap -dump:format=b,file=heap.hprof <pid>
导出堆转储 -
使用MAT分析支配树
-
定位到具体代码位置
高频Full GC解决方案
// 典型错误示例:不当的缓存实现 public class LeakyCache { private static Map<Long, Object> cache = new HashMap<>(); public void put(Long id, Object data) { cache.put(id, data); // 无限增长的缓存 } }
调优策略:
-
检查
-Xmx
设置是否过小 -
添加
-XX:+HeapDumpOnOutOfMemoryError
自动转储 -
使用WeakHashMap或Guava Cache
-
调整老年代回收阈值
六、未来趋势展望
-
无感知GC:向Submillisecond Pause迈进
-
AI驱动的自适应收集:动态调整策略
-
异构内存管理:NVMe SSD作为扩展内存
-
云原生GC:Kubernetes感知的资源调度
拓展阅读推荐:
《深入理解Java虚拟机(第3版)》周志明著
美团技术团队《G1垃圾收集器调优实践》