垃圾收集器
@TOC
垃圾收集算法
我们在处理虚拟机的垃圾收集的时候,会根据对象的存活年龄,将内存分配为几块。一般将java的堆分为Eden和Old。这样就可以根据不同的分代,选取合适的垃圾收集算法。
例如,在Eden区大量的对象都是朝生夕死,每次收集都有大量的对象死去。所以可以选择标记复制算法,只需要付出少量的复制成本就能完成垃圾收集。而老年代的对象存活几率比较高,而且没有额外的空间对他进行担保,所以我们必须选择标记清楚或者标记整理算法进行垃圾收集。但注意:标记清除或 标记整理算法,会比复制算法慢10倍以上。
标记-复制算法:
解析:通过GC可达性分析,判断出若干存活对象,然后将所有存活对象复制到保留内存,并将左侧部分全部内存清理。 问题:浪费空间:必须有保留内存空间。所以标记复制算法主要用在年轻代,大多数对象朝生夕死。并不会用于老年代,因为移动大对象会消耗相当的时间和空间。
标记清除算法:
问题: 1、效率问题:如果需要标记的对象特别多,或者内存特别大,会消耗相当的时间。 2、空间问题:会产生大量的碎片空间,影响性能。
标记整理算法:
标记整理算法是根据老年代的特点置顶的一种收集算法,标记之后并不是直接清除可回收对象,而是将标记对象向一端 整理,然后清理边界以外的内存。
不同种类的垃圾收集器
市面上的垃圾收集器 ,目前大概10种。
Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)
Serial(串行)收集器是最基本的,历史最悠久的垃圾收集器。他是一个单线程的垃圾收集器,他在垃圾收集的时候,会造成STW(Stop The World)。其新生代采用复制算法,,老年代采用标记-整理算法,能收集的内存区域,在几十-几百M的内存。
优势:简单而高效(与其他收集器的单线程相比)。同时Serial收集器没有切换线程所带来的开销,自然可以获得非常高的单线程效率。
Serial Old收集器作为Serial收集器的老年代版本,同样是一个单线程的收集器。它主要有两大用途: 1、JDK1.5之前的版本中,与parallel收集器搭配使用 2、作为CMS收集器的后备方案。
Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),
-XX:+UseParallelOldGC(老年代)) Parallel其实是Serial的多线程版本,除了使用多线程进行垃圾收集之外,其余的行为(控制参数,算法,回收策略)和Serial收集器类似。默认的收集线程数量就是CPU的核心数量。当然也可以通过-XX:ParallelGCThreads 指定线程数,但一般不推荐修改。 Parallel收集器的关注点是吞吐量(高效利用CPU)。CMS等垃圾收集器,则更多的是关注用户线程的停顿时间,提高用户体验。Parallel收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量。但是如果对收集器运作机制不是很了解,可以把内存管理优化交给虚拟机去完成也是OK的。 新生代采用复制算法,老年代采用标记整理算法
Parallel Old是Parallel的老年代版本。使用多线程和”标记-整理“算法。在注重吞吐量及CPU资源的场合,都可以优先考虑Parallel收集器和Parallel Old收集器。(JDK8 默认的新生代和老年代收集器) Parallel垃圾收集器,无法与CMS兼容使用,但是Parallel New可以。 ParNew收集器(-XX:+UseParNewGC)
ParNew收集器其实跟Parallel收集器很类似,区别主要在于它可以和CMS收集器配合使用。 新生代采用复制算法,老年代采用标记-整理算法。 它是许多运行在Server模式下的虚拟机的首要选择,除了Serial收集器外,只有它能与CMS收集器(真正意义上的并发收集器,后面会介绍到)配合工作。
CMS收集器(-XX:+UseConcMarkSweepGC(old))
CMS(Concurrent Mark Sweep)收集器,是一种以获取最短停顿时间为目标的收集器。它比较符合以用户体验为主的垃圾收集器,也是HotSpot上第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程基本同时工作。
CMS整体收集过程:
1、初始标记:暂停所有其他线程(STW),然后从所有GC root出发,标记所有GC root直接引用的对象。 2、 并发标记:并发标记阶段就是从 GC ROOTS直接关联的对象(初始标记的对象)开始遍历整个对象图,这个过程耗时很长,但不用STW。不过用户线程与垃圾收集线程并发会导致已经标记的对象状态有所改变。 3、重新标记:重新修正并发标记中状态变化的一部分对象。这个阶段会STW,但时间比并发标记进行的时间要短很多,但会比处室标记稍长。这个阶段主要通过三色标记里的增量更新算法。 4、并发清理:继续进行用户线程,同时进行清理。这个阶段如果有新增的对象,会被标记为黑色,不做任何处理(三色标记)。 5、并发重置:重置本次GC过程中的标记数据。
CMS优势:
1、并发收集 2、低停顿 缺陷: 1、对CPU资源敏感(会和服务争抢资源) 2、无法处理浮动垃圾(在并发标记和并发清理阶段又产生的新垃圾,这种垃圾只能下一次GC在做清理。) 3、它使用的标记清除算法,会产生大量碎片空间,当然可以通过-XX:+UseCMSCompactAtFullCollection让他在清除后进行整理。 4、执行过程的不确定性:存在上一次垃圾回收还没执行完,垃圾回收又会被触发的情况,特别是在并发标记和并发清理的阶段。一边回收,系统一边运行,也许还没回收完就会触发FullGC,就会产生”concurrent mode failure“,此时会进行STW,由serial old 垃圾收集器来回收
CMS核心参数:
-XX:+UseConcMarkSweepGC:启用cms
-XX:ConcGCThreads:并发的GC线程数
-XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(减少碎片)
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次
-XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
-XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
-XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,降低CMS GC标记阶段(也会对年轻代一起做标记,如果在minor gc就干掉了很多对垃圾对象,标记阶段就会减少一些标记时间)时的开销,一般CMS的GC耗时 80%都在标记阶段
-XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW
-XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;