常见的垃圾收集器
上一篇文章中介绍了JVM的GC常见算法,而JVM的垃圾收集器就是这些算法使用编程语言的具体实现,介绍算法的时候,我们默认算法的执行都是单线程的,但是涉及到具体的语言实现就不只是单线程了,具体分为一下三种模式:
-
串行收集器:一条GC线程,运行的时候需要暂停用户程序(stop the world),包括一下实现:
serial:
- 特点:用于新生代,采用复制算法,在JDK 1.3.1之前是虚拟机新生代收集的唯一选择
- 开启方式:-XX:+UseSerialGC(client模式默认值)
serial old:
- 特点: 用于老年代,采用标记/整理算法(也是CMS的备用方案,当CMS产生很多内存碎片的时候,使用进行内存碎片的整理),Serial收集器是虚拟机运行在Client模式下的默认新生代收集器
- 开启方式:-XX:+UseSerialGC(client模式默认值)
-
并行收集器:多条GC线程,且运行时也需要(stop the world),包括一下实现:
ParNew:
- 特点:用于新生代,采用复制算法,算是Serial收集器的多线程版本
- 使用场景:ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器。很重要的原因是:除了Serial收集器外,目前只有它能与CMS收集器配合工作。
- 开启方式:-XX:+UseParNewGC
Parallel Scavenge:
- 特点:用于新生代,基本上和ParNew一样,不同可设置线程数,新生代自适应大小等参数来让控制更灵活,强调吞吐量
-
使用参数:
- -XX:+UseParallelGC(开启)
- -XX:ParallelGCThreads=4(指定线程数)
- -XX:UseAdaptiveSizePolicy(内存区域自适应,开启后不需要手工指定新生代的大小、Eden与Survivor区的比例、晋升老年代对象年龄等细节参数了,这种调节方式称为GC自适应的调节策略GC Ergonomics)
Parallel old:
- 特点:用于年老代,采用标记/整理算法,在开启parallel scavenge之后默认的年老代搜集器,强调吞吐量
- 使用参数:-XX:-UseParallelOldGC开启
-
并发收集器:一条或多条GC线程,且它需要在部分阶段(stop the world),部分阶段与用户程序并发执行,包括一下实现:
CMS:
- 特点:强调的是停顿时间最短,用于年老代,采用标记/清除算法,默认是不会回收Perm区
- 优点:并发收集、低停顿。
-
缺点:
- 会抢占CPU资源,降低吞吐量
- 使用标记清除算法,意味着收集结束时会有大量空间碎片产生,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。
- 无法处理浮动垃圾:由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。
也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了
-
执行过程:
- 初始标记:仅只是标记一下GC Roots能直接关联到的对象,速度很快,需要Stop the World
- 并发标记:就是进行GC Roots Tracing的过程
- 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录仍然需要“Stop The World”。
- 并发清除:恢复应用程序,并发清除未标记的垃圾对象。
- 使用参数:
-
G1:
- 特点: 用于新生代和年老代,采用标记整理算法和复制算法,HotSpot开发团队开发的初衷是为了未来代替CMS的
- 特点:G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内可以获取尽可能高的收集效率
同时根据新生代和老年代使用不同实现的组合
上图是作用于不同分代的收集器,如果两个收集器之间存在连线,就说明它们可以搭配使用。虚拟机所处的区域,则表示它是属于新生代收集器还是老年代收集器。
各版本默认的垃圾回收器
- jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
- jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
- jdk1.9 默认垃圾收集器G1
优化
- 什么情况会出现STW的Full GC呢?
- 永久代Perm空间不足
- CMS GC的时出现promation failed 和concurrent mode failure(出现concurrent mode failure的原因一般是CMS正在进行,但由于老年代空间不足,要尽快回收老年代里面的不再使用的对象,这时停止所有线程,同时终止CMS ,直接进行 Serial Old GC)
- 统计Young GC 普及到老年代的平均大小大于老年代的剩余空间的时候
- 主动出发Full GC(执行jmap -histo:live[pid])来避免碎片问题
网友评论