本节讲解一下常见的垃圾回收器。需要特别注意的是,每一种垃圾回收器都会存在用户线程(即用户程序)暂停的问题,只不过每种回收器用户线程暂停的时长优化程度不一样。在启动JVM时,可以通过“指定参数-xx:+垃圾回收器名称”来自定义JVM使用何种垃圾回收器进行垃圾回收。如果未指定的话,JVM将根据服务器的CPU核数和JDK版本,自动选择对应的默认垃圾回收器。
(1)Serial(-XX:+UseSerialGC)
这是一个单线程运行的串行垃圾收集器,是JVM中最基本、比较早期的垃圾回收器。在JDK1.3之前是JVM唯一的新生代垃圾回收器。当JVM需要进行垃圾回收的时候,会暂停所有的用户线程直到垃圾回收结束。它采用复制算法进行垃圾回收。
(2)SerialOld(-XX:+UseSerialGC)
这是串行垃圾回收器的老年代回收器版本,同样是单线程运行的垃圾回收器。它采用标记-整理算法进行垃圾回收。
(3)ParNew(-XX:+UseParNewGC)
串行垃圾回收器的多线程并行运行版本。它采用复制算法进行垃圾回收。由于采用多线程运行,因此如果服务器是单核CPU的,那么其效率会远低于单线程串行垃圾回收器。
(4)ParallelScavenge(-XX:+UseParallelGC)
和ParNew 回收器有点类似,它是一个新生代收集器,俗称吞吐量优先收集器。它采用复制算法进行垃圾回收。所谓吞吐量就是CPU用于运行用户代码(用户线程)的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码(用户线程)时间 / (运行用户代码时间 + 垃圾收集时间)。
(5)ParallelOld(-XX:+UseParallelOldGC)
是老年代的并行垃圾回收器,是老年代吞吐量优先的回收器,和ParNew很类似。它采用标记-整理算法进行垃圾回收。在服务器CPU核数较多的情况下,可以优先考虑使用该回收器。
(6)CMS (-XX:+UseConcMarkSweepGC)
这是一个多线程并发低停顿的老年代垃圾回收器,全称为Concurrent Mark Sweep,简称CMS。如果响应时间的重要性需求大于吞吐量要求并且要求服务器响应速度高的情况下,建议优先考虑使用此垃圾回收器。CMS垃圾回收器用两次短暂的暂停来代替串行或并行标记-整理算法时出现的长暂停。它采用标记-清除算法进行垃圾回收,因此很容易产生内存碎片,但是CMS回收器做了一些小的性能优化,优化措施是把未分配的内存空间汇总成一个内存地址列表,当JVM需要分配内存空间时会搜索这个列表,找到符合条件的内存空间来存储这个对象,如果寻找不到符合条件的内存空间,就会产生Full GC。并且由于该垃圾回收器是多线程并发的,很多时候GC线程和用户应用程序线程是并发执行的,因此垃圾回收时会占用很高的CPU资源。
(7)G1-GarbageFirst(-XX:+UseG1GC)
这是JVM新推出的垃圾回收器,同时支持新生代和老年代回收,能充分利用多核CPU的硬件优势,可以并行来缩短用户线程停顿的时间,也可以并发让垃圾收集与用户程序同时进行。该垃圾回收器虽然保留了传统的分代概念,但JVM堆的内存布局已经和传统的JVM堆布局不一样了,G1将整个堆划分为很多个大小相等的独立Region区域, 新生代和老年代不再是被隔离开的,它们都是一部分不需要连续的Region区域的集合。G1同时采用标记-整理和复制等多种回收算法进行垃圾回收。
本文节选自《软件性能测试、分析与调优实践之路(第2版)》,获作者和出版社授权发布。