JVM-分代垃圾回收器

次收集器

Scavenge GC,指发生在新生代的GC,因为新生代的Java对象大多都是朝生夕死,所以Scavenge GC非常频繁,一般回收速度也比较快。当Eden空间不足以为对象分配内存时,会触发Scavenge GC。 

一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。 

当年轻代堆空间紧张时会被触发 

相对于全收集而言,收集间隔较短

全收集器

Full GC,指发生在老年代的GC,出现了Full GC一般会伴随着至少一次的Minor GC(老年代的对象大部分是Scavenge GC过程中从新生代进入老年代),比如:分配担保失败。Full GC的速度一般会比Scavenge GC10倍以上。当老年代内存不足或者显式调用System.gc()方法时,会触发Full GC

垃圾回收器的常规匹配

串行收集器(Serial)

Serial收集器是Hotspot运行在Client模式下的默认新生代收集器, 它的特点是:只用一个CPU(计算核心)/一条收集线程去完成GC工作, 且在进行垃圾收集时必须暂停其他所有的工作线程(Stop The World-后面简称STW)。可以使用-XX:+UseSerialGC打开。 

虽然是单线程收集, 但它却简单而高效, VM管理内存不大的情况下(收集几十M~一两百M的新生代), 停顿时间完全可以控制在几十毫秒~一百多毫秒内。 

并行收集器(ParNew

ParNew收集器其实是前面Serial的多线程版本, 除使用多条线程进行GC, 包括Serial可用的所有控制参数、收集算法、STW、对象分配规则、回收策略等都与Serial完全一样(也是VM启用CMS收集器-XX: +UseConcMarkSweepGC的默认新生代收集器)

由于存在线程切换的开销, ParNew在单CPU的环境中比不上Serial, 且在通过超线程技术实现的两个CPU的环境中也不能100%保证能超越Serial. 但随着可用的CPU数量的增加, 收集效率肯定也会大大增加(ParNew收集线程数与CPU的数量相同, 因此在CPU数量过大的环境中, 可用-XX:ParallelGCThreads=<N>参数控制GC线程数)

Parallel Scavenge收集器

ParNew类似, Parallel Scavenge也是使用复制算法, 也是并行多线程收集器. 但与其他收集器关注尽可能缩短垃圾收集时间不同, Parallel Scavenge更关注系统吞吐量

系统吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间

停顿时间越短就越适用于用户交互的程序-良好的响应速度能提升用户的体验;而高吞吐量则适用于后台运算而不需要太多交互的任务-可以最高效率地利用CPU时间,尽快地完成程序的运算任务. Parallel Scavenge提供了如下参数设置系统吞吐量:

Serial Old收集器

Serial OldSerial收集器的老年代版本, 同样是单线程收集器,使用“标记-整理”算法

Parallel Old收集器

Parallel OldParallel Scavenge收集器的老年代版本, 使用多线程和“标记-整理”算法, 吞吐量优先, 主要与Parallel Scavenge配合在注重吞吐量及CPU资源敏感系统内使用;

CMS收集器(Concurrent Mark Sweep

CMS(Concurrent Mark Sweep)收集器是一款具有划时代意义的收集器, 一款真正意义上的并发收集器, 虽然现在已经有了理论意义上表现更好的G1收集器, 但现在主流互联网企业线上选用的仍是CMS(Taobao、微店). 

CMS是一种以获取最短回收停顿时间为目标的收集器(CMS又称多并发低暂停的收集器), 基于”标记-清除”算法实现, 整个GC过程分为以下4个步骤

1. 初始标记(CMS initial mark) 

2. 并发标记(CMS concurrent mark: GC Roots Tracing过程

3. 重新标记(CMS remark) 

4. 并发清除(CMS concurrent sweep: 已死对象将会就地释放, 注意:此处没有压缩

其中13两个步骤(初始标记、重新标记)仍需STW. 但初始标记仅只标记一下GC Roots能直接关联到的对象, 速度很快; 而重新标记则是为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录, 虽然一般比初始标记阶段稍长, 但要远小于并发标记时间.

CMS特点: 

1. CMS默认启动的回收线程数=(CPU数目+3)4 

CPU>4, GC线程一般占用不超过25%CPU资源, 但是当CPU<=4, GC线程可能就会过多的占用用户CPU资源, 从而导致应用程序变慢, 总吞吐量降低

2.无法处理浮动垃圾, 可能出现Promotion FailureConcurrent Mode Failure而导致另一次Full GC的产生: 浮动垃圾是指在CMS并发清理阶段用户线程运行而产生的新垃圾. 由于在GC阶段用户线程还需运行, 因此还需要预留足够的内存空间给用户线程使用, 导致CMS不能像其他收集器那样等到老年代几乎填满了再进行收集. 因此CMS提供了-XX:CMSInitiatingOccupancyFraction参数来设置GC的触发百分比(以及

-XX:+UseCMSInitiatingOccupancyOnly来启用该触发百分比), 当老年代的使用空间超过该比例后CMS就会被触发(JDK 1.6之后默认92%). 但当CMS运行期间预留的内存无法满足程序需要, 就会出现上述Promotion Failure等失败, 这时VM将启动后备预案: 临时启用Serial Old收集器来重新执行Full GC(CMS通常配合大内存使用, 一旦大内存转入串行的Serial GC, 那停顿的时间就是大家都不愿看到的了).

3.最后, 由于CMS采用”标记-清除”算法实现, 可能会产生大量内存碎片. 内存碎片过多可能会导致无法分配大对象而提前触发Full GC. 因此CMS提供了-XX:+UseCMSCompactAtFullCollection开关参数, 用于在Full GC后再执行一个碎片整理过程. 但内存整理是无法并发的, 内存碎片问题虽然没有了, 但停顿时间也因此变长了, 因此CMS还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction用于设置在执行N次不进行内存整理的Full GC, 跟着来一次带整理的(默认为0: 每次进入Full GC时都进行碎片整理).

分区收集- G1收集器

G1(Garbage-First)是一款面向服务端应用的收集器, 主要目标用于配备多颗CPU的服务器治理大内存

- G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS). 

-XX:+UseG1GC启用G1收集器

与其他基于分代的收集器不同, G1将整个Java堆划分为多个大小相等的独立区域(Region), 虽然还保留有新生代和老年代的概念, 但新生代和老年代不再是物理隔离的了, 它们都是一部分Region(不需要连续)的集合.如:

每块区域既有可能属于O区、也有可能是Y, 因此不需要一次就对整个老年代/新生代回收. 而是当线程并发寻找可回收的对象时, 有些区块包含可回收的对象要比其他区块多很多. 虽然在清理这些区块时G1仍然需要暂停应用线程, 但可以用相对较少的时间优先回收垃圾较多的Region. 这种方式保证了G1可以在有限的时间内获取尽可能高的收集效率

G1的新生代收集跟ParNew类似: 存活的对象被转移到一个/多个Survivor Regions. 如果存活时间达到阀值, 这部分对象就会被提升到老年代.如图:

其特定是: 

一整块堆内存被分为多个Regions. 

存活对象被拷贝到新的Survivor区或老年代

年轻代内存由一组不连续的heap区组成, 这种方法使得可以动态调整各代区域尺寸

Young GC会有STW事件, 进行时所有应用程序线程都会被暂停

多线程并发GC. 

G1老年代GC特点如下

并发标记阶段 

1 在与应用程序并发执行的过程中会计算活跃度信息

2 这些活跃度信息标识出那些regions最适合在STW期间回收(which regions will be best to reclaim during an evacuation pause). 

3 不像CMS有清理阶段

再次标记阶段 

1 使用Snapshot-at-the-Beginning(SATB)算法比CMS快得多

2 region直接被回收

拷贝/清理阶段(Copying/Cleanup Phase) 

1 年轻代与老年代同时回收

2 老年代内存回收会基于他的活跃度信息

猜你喜欢

转载自www.cnblogs.com/yintingting/p/11324096.html