0. 分代收集
当前大多数商业虚拟机的垃圾收集器都遵循“分代收集”。就是针对不同的内存区域使用不同的数计算法。但是已经有能够全区域收集不分代的收集器了。因为不同的区域有不同的特点,为是各个区域达到最好的效果,所以进行分代。
未来会不会不分代的虚拟机主导呢,真是的,@_@学习的速度赶不上技术发展的速度。。。听说隔壁 Rust 已经不用垃圾收集了!!
-
部分收集(Partial GC):不是针对完整的 java 堆的垃圾收集
- 新生代收集(Minor GC / Young GC):只针对新生代的垃圾收集
- 老年代收集(Major GC / Old GC):只针对老年代的垃圾收集
- 混合收集(Mixed GC):针对
整个新生代
+部分老年代
的垃圾收集
- 整堆收集(Full GC):针对整个 java 堆区的垃圾收集,包括新生代、老年代、方法区
0.1 新生代收集(Minor GC / Young GC)
只针对新生代的垃圾收集
触发条件:
Eden 满了或不够分配对象了,survivor 满了不会触发,会等到Eden 满了一起收集。
细节
MinorGC 有个细节,就是 MinorGC 之前,会做一次检查,检验 老年代的最大连续空间 是否大于 新生代所有对象的总和。如果大于,则说明 MinorGC 是安全的,进行 MinorGC;否则就直接进行 FullGC。另外空间担保策略(-XX:HandlePromotionFailure)在 jdk7 之后已经失效了。
正常情况下,是 GC 年龄达到阈值后会移动到老年代,但是有两种特殊情况:
- 大对象,Eden 区放不下的情况下,会进行一次 youngGC,如果 GC 之后还放不下就直接进入老年代;或者 Eden 区的幸存者,survivor 区存不下,也会直接晋升到老年代
- 当同一年龄的对象总和大于 survivor 的一般时,大于等于该年龄的对象就会直接进入老年代,而不用等到阈值年龄
特点:
- 频率较高
程序运行过程中会持续创建对象,且大多数对象都是使用一次就结束了,所以新生代收集比较频繁 - 速度快
占用内存较小,另外复制算法效率也较高 - stop the world
垃圾收集期间,会暂停用户线程,等垃圾回收结束在回复用户线程,但是因为回收速度较快,所以影响较小
0.2 老年代收集(Major GC / Old GC)
只针对老年代的垃圾收集,目前只有 CMS 有单独收集老年代的行为
- stop the world
- 速度缓慢,是 minorGC 的 10 倍以上
- MajorGC 后空间还不足,则 OOM
很多时候会和 FullGC 混淆,需要具体问题具体分析
0.3 混合收集(Mixed GC)
针对整个新生代
+ 部分老年代
的垃圾收集,目前只有 G1 收集器有混合收集
0.4 整堆收集(Full GC)
针对整个 java 堆区的垃圾收集,包括新生代、老年代、方法区
触发条件:
- System.gc()
- 老年代空间不足
- 方法去不足
- minorGC 判定为幸存的、要移动到老年代的对象大于老年代可用空间
- eden 区的幸存者,survivor 存放不下,需要移动到老年代,但是可用空间不足
fullGC 是尽量要避免的,下图是对象分配和 GC 的简图
1. 垃圾收集器
常用垃圾收集器- Serial + Serial Old
较早的垃圾收集器组合,垃圾收集是单线程完成,用于早期应用程序小、内存占用小的时期。
GC 期间会停止用户线程,俗称 stop the world(stw)
适用场景:适用于服务器性能差的情况,例如服务器只有 2 核
- Parallel Scanvege + Parallel Old
随着应用程序体量的增加,内存占用原来越大,单线程收集垃圾缓慢,伴随着更长的 stw,所以出现了多线程收集器,Parallel + Parallel Old 就是多线程 GC 的收集器组合,同时也是 jdk8 默认的收集器。
GC 期间也会 stw。
适用场景:注重吞吐量的场景
- CMS + ParNew(Parallel Scanvege 的增强版)
多线程 GC 虽然降低了收集时间,但仍然存在 stw,于是诞生了 Concurrent 的收集器,用户线程和 GC 线程可以并发执行。CMS 就是这类收集器的最早实践者。
并发收集包括以下几个阶段
- 初始标记
stw - 并发标记
三色标记算法待补充 - 重新标记
stw - 并发清理
适用场景:与 G1 类似,注重低延迟的情况
- G1、ZGC、Shenandoah
都是并发收集
网友评论