常见的垃圾收集器有Serial GC、ParNew GC、CMS GC、Parallel GC、G1 GC
Serial GC:
- 优点:单线程精简的GC实现,无需维护复杂的数据结构,初始化简单,是client模式下JVM默认选项。最古老的GC。老年代使用Serial Old GC
- 缺点:会进入"Stop-The World"状态。
- 命令:-XX: UseSerialGC
- 日志:DefNew是新生代,Tenured是老年代
ParNew GC:
- 新生代GC实现,是SerialGC的多线程版本,最常见的应用场景是配合老年代的CMS GC 工作。
- 命令:-XX: UseParNewGC
- 日志:ParNew是新生代,Tenured是老年代
CMS(Concurrent Mark Sweep)GC:
- 优点: 基于标记-清除(Mark-Sweep)算法,尽量减少停顿时间(停顿优先)。
- 缺点: 存在碎片化问题,在长时间运行的情况下会发生full GC,导致恶劣停顿。会占用更多的CPU资源,和用户争抢线程。在JDK 9中被标记为废弃。
- 命令: 老年代使用CMS GC。同时年轻代 触发多线程ParNew GC
- 日志: ParNew是新生代,CMS是老年代
Parrallel GC:
- 在JDK8等版本中,是server模式JVM的默认GC选择,也被称为吞吐量优先的GC,算法和Serial GC相似。(吞吐量优先的 GC)
- 特点是老生代和新生代GC并行进行,更加高效。
- 命令:-XX: UseParallelGC (表明新生代用Parrallel GC) -XX: UseParallelOldGC (表明老年代使用Parrallel Old GC) 二者可以相互激活。
- 日志:PSYoungGen是新生代,ParOldGen是老年代。
G1 GC:
- 兼顾了吞吐量和停顿时间的GC实现,是Oracle JDK 9后默认的GC
- 可以直观的设值停顿时间,相对于CMS GC ,G1未必能做到CMS最好情况下的延时停顿,但比最差情况要好得多
- G1 仍存在分代的概念,使用了Region棋盘算法,实际上是标记-整理(Mark-Compact)算法,可以避免内存碎片,尤其是堆非常大的时候,G1优势更明显。
- G1 吞吐量和停顿表现都非常不错。
- 命令:-XX: UseG1GC
- 日志:garbage-first heap
ZGC:
- 持TB级别:根据官方文档来看,在Jdk11时ZGC可支持的最大内存为4TB,在jdk⒀可以支持16TB。持TB级别:根据官方文档来看,在Jdk11时ZGC可支持的最大内存为4TB,在jdk⒀可以支持16TB。
- 最大停顿时间不超过10ms:之所以能控制在10ms以下,是因为它的停顿时间主要跟Root扫描有关,而跟root数量和堆的大小没有关系。
- ZGC内部是以Region的方式进行内存布局的,暂时没有设置分代,使用读屏障、颜色指针等技术来实现可并发的标记–整理算法的,且低延迟,Region分为大中小三种类型: 1、小型Region(small Region):容量为2mb,用于放置小于256kb的对象。 2、中型Region(medium Region):容量为4mb,用于放置容量大于等于256kb但小于4mb的对象。 3、大型Region(large Region):容量不固定,大小可变,但必须是2的整数倍,且大小大于等于4mb的对象。
- 命令:-XX: UnlockExperimentalVMOptions -XX: UseZGC
那么还有Young GC 、Old GC、Mixed GC、Full GC又是什么呢?
首先这些GC不是回收器,而是垃圾收集,其实正对对HotSpot VM的实现, 里面的GC按照回收区域又分两大类型:
- 部分收集Partial GC 1、新生代收集 Minor GC / Young GC:新生代(EdenS0,S1)的垃圾收集,在年轻代中的Eden内存区域被占满之后,实际上就需要触发年轻代的gc 2、老年代收集 Major GC / Old GC:老年代GC(目前只有CMS GC 有单独的老年代收集的行为)经常说Full GC 就是老年代收集 3、混 合 收 集 Mixed GC:收集整个新生代和部分老年代的 垃圾收集(目前只有G1 GC 有这种收集的行为)
- 整堆收集Full GC:收集整个JAVA堆和方法区的垃圾收集
Young GC 、Old GC、Mixed GC、Full GC触发时间
- 新生代收集 Minor GC / Young GC:一般就是在新生代的Eden区域满了之后就会触发,采用复制算法来回收新生代的垃圾。
- 老年代收集 Major GC / Old GC
- 老年代可用的连续内存空间 < Young GC后升入老年代平均值,说明本次Young GC后可能升入老年代的对象大小,可能超过了老年代当前可用内存空间。此时必须先触发一次Old GC给老年代腾出更多的空间,然后再执行Young GC。
- 执行Young GC之后有一批对象需要放入老年代,老年代就是没有足够的内存空间存放这些对象,此时必须立即触发一次Old GC。
- 老年代内存使用率超过了92%,也要直接触发Old GC,当然这个比例是可以通过参数调整
- 整堆收集的Full GC:
- JVM的实现机制里,其实在Old GC几种条件达到的时候,他触发的实际上就是Full GC,这个Full GC会包含Young GC、Old GC和永久代的GC。
- 方法区不足就触发Full GC
- System.gc()
- 混 合 收 集 Mixed GC:是只有G1垃圾收集器才会触发的, mixed gc是指新生代gc+部分老年代gc,注意是部分,不是对整个老年代进行gc。当老年代的内存使用率达到某个阈值就会触发混合GC
如何选择GC(JKD9之前的版本)
- 如果你的程序追求低延迟,用户交互度较为频繁,那你可以采用
ParNew CMS
组合(这也是淘宝早期的选择,但后面采用了自研JVM)。 - 如若你的程序追求高吞吐,后台计算工作较多,那么
Parallel Scavenge Parallel Old
这组PS PO
的收集器会更适合你。 - 单核或双核的机器时,那么最经典的
Serial Serial Old
组合绝对是你的最佳选择。