概念
是一种自动管理内存的机制,主要功能是自动回收程序运行过程中不再使用的内存,以防止内存泄漏并提高内存使用率;
回收的目标
- 不再被引用的对象: 孤岛数据一般会在YGC阶段就被回收了
- 大对象或长期对象: 大对象通常直接分配在老年代,这部分数据在FGC回收 (所以应当尽量避免创建大对象)
回收器类别
- SerialGC
单线程串行处理;低开销但是每次回收都会暂停所有应用线程 - ParallelGC - JDK8默认
追求高吞吐,不太适合web服务(STW可能会很长);
多线程回收处理;SerialGC的优化,虽然使用多线程提高了吞吐,但是STW仍然存在(YGC和FGC都会暂停所有线程)。 - CMS
相对于ParallelGC和G1,更适合中小内存要求高响应速度(低停顿)的应用场景;
可能会引入额外的CPU开销(并发标记和清除)和内存碎片(未开启并发压缩时) - G1
使用增量式回收的策略,通过将垃圾回收过程分成多个小阶段来减少停顿时间‘;
优点:
通过最大停顿时间目标,G1 GC 提供了对停顿时间的更好控制。
(-XX:MaxGCPauseMillis=<milliseconds>);
缺点: 更多的内存消耗(需要额外的内存来管理 区域划分、每个区域的垃圾情况和回收情况等)、配置复杂 - ZGC 11以上支持
- Shenandoah 12以上支持
总结: JDK11以下直接G1 , 11及以上 用最新的;内存资源紧张时考虑用CMS;高吞吐考虑用ParallelGC
复制算法
分为From区和To区, 标记From中所有活动对象移动到To区,下一次再反过来,每移动一次对象的计数+1; 达到指定计数后,该对象晋升到老年代; 计数可以通过-XX:MaxTenuringThreshold进行指定,通常默认15次
优点: 高效、连续内存、算法简单、快速回收(只需要移动From区的活动对象)
缺点: 两倍的内存开销
标记-清除
识别所有GC Roots可达对象,标记后清除所有未标记,此时清除后会存在空间碎片
标记-整理
在标记-清除的基础上,增加一个整理阶段,将活动对象移动到内存的一端,释放出连续的空闲内存区域
CMS回收过程
- 初始标记阶段: 标记从GC Roots直接可达对象 (需要STW)
- 并发标记阶段: 扫描整个堆,标记所有从初始标记的对象可以到达的对象
- 重新标记阶段: 标记在并发标记阶段可能发生的对象变动(STW)
- 跟踪变动:JVM 可能会维护一种机制(例如,修改记录或变化记录)来跟踪对象引用的变动。这些机制可以在并发标记期间记录发生的变动,以便在重新标记阶段处理。
- 更新根集:重新标记阶段会扫描根对象的引用,确保所有根对象的引用都被正确标记。这样可以确保由于应用线程的变动,任何新的可达对象都被标记。
- 增量标记:某些 JVM 实现可能会使用增量标记的方法,通过记录在并发标记阶段发生的对象引用变化,来减少重新标记阶段的工作量。
- 并发清除: 清除未标记对象
- 并发压缩: 存活对象移动到堆的一端,清除内存碎片(可选)
G1回收过程
与CMS类似,分三个阶段进行标记: 初始标记、并发标记和重新标记; 回收阶段则选择垃圾最多的区域进行回收;