JAVA垃圾收集器之CMS收集器

1、特点

CMS收集器是JAVA虚拟机中垃圾收集器的一种。它运行在JAVA虚拟机的老年代中。CMS是(Concurrent
MarkSweep)的首字母缩写。CMS收集器是一种以获取最短回收停顿时间为目标的收集器。比较适用于互联网等场合,可能是互联网中最重要的收集器模式;

2、优点

由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的。因此CMS是一款优秀的收集器,具备了并发收集、低停顿的优点,Sun的一些官方文档里面也称之为并发低停顿收集器(Concurrent
Low Pause Collector)。

3、缺点

CMS收集器对CPU资源非常敏感

面向并发设计的程序都对CPU资源比较敏感。在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。CMS默认启动的回收线程数是(CPU数量+3)/
4,也就是当CPU在4个以上时,并发回收时垃圾收集线程最多占用不超过25%的CPU资源。但是当CPU不足4个时(譬如2个),那么CMS对用户程序的影响就可能变得很大,如果CPU负载本来就比较大的时候,还分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然降低了50%,这也很让人受不了。为了解决这种情况,虚拟机提供了一种称为“增量式并发收集器”(Incremental
Concurrent Mark Sweep / i-CMS)的CMS收集器变种,所做的事情和单CPU年代PC机操作系统使用抢占式来模拟多任务机制的思想一样,就是在并发标记和并发清理的时候让GC线程、用户线程交替运行,尽量减少GC线程的独占资源的时间,这样整个垃圾收集的过程会更长,但对用户程序的影响就会显得少一些,速度下降也就没有那么明显,但是目前版本中,i-CMS已经被声明为“deprecated”,即不再提倡用户使用。

CMS收集器无法处理浮动垃圾

由于CMS并发清理阶段用户线程还在运行着,伴随程序的运行自然还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理掉它们,只好留待下一次GC时再将其清理掉。这一部分垃圾就称为“浮动垃圾(Floating
Garbage)”。也是由于在垃圾收集阶段用户线程还需要运行,即还需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。在默认设置下,CMS收集器在老年代使用了68%的空间后就会被激活,这是一个偏保守的设置,如果在应用中老年代增长不是太快,可以适当调高参数-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比,以便降低内存回收次数以获取更好的性能。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent
Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。所以说参数-XX:CMSInitiatingOccupancyFraction设置得太高将会很容易导致大量“Concurrent
Mode Failure”失败,性能反而降低。

收集结束时会产生大量空间碎片

CMS是一款基于“标记-清除”算法实现的收集器,在收集结束时会产生大量空间碎片。空间碎片过多时,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大的空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full
GC。为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full
GC服务之后额外免费附送一个碎片整理过程,内存整理的过程是无法并发的。空间碎片问题没有了,但停顿时间不得不变长了。虚拟机设计者们还提供了另外一个参数-XX: CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full
GC后,跟着来一次带压缩的。

4、代码

package com.gc;

import java.util.ArrayList;

import java.util.List;

/**

* 简单的JAVA虚拟机内存回收,cms收集器的使用

* 参数:-Xms30m -Xmx60m-Xmn10m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails

* @author 范芳铭

*/

public class EasyCMS {

public byte[] placeHolder =new byte[64 * 1024]; //占位符

public static voidmain(String[] args) throws Exception{

outOfMemoryByExpansionSize();

}

private static voidoutOfMemoryByExpansionSize() throws Exception{

List<EasyCMS>list = new ArrayList<EasyCMS>();

while(true){

EasyCMS serial =new EasyCMS();

list.add(serial);

Thread.sleep(10);//停顿10毫秒

}

}

}

5、参数

参数:-Xms30m -Xmx60m-Xmn10m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails

-Xms30m  JVM最小30M

-Xmx100m  JVM最大100M

-Xmn10m  新生代固定10M

-XX:+ UseConcMarkSweepGC对应parNew + cms
收集器

-XX:+PrintGCDetails 打印详细GC

6、运行结果

[GC [ParNew: 9110K->972K(9216K),0.0056278 secs] 48708K->48702K(60416K), 0.0056664 secs] [Times: user=0.00sys=0.00, real=0.01 secs]

[GC [1 CMS-initial-mark:47730K(51200K)] 48766K(60416K), 0.0000829 secs] [Times: user=0.00 sys=0.00,real=0.00 secs]

[CMS-concurrent-mark: 0.003/0.003secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[CMS-concurrent-preclean:0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[GC[YG occupancy: 1036 K (9216K)][Rescan (parallel) , 0.0001092 secs][weak refs processing, 0.0000140 secs][1 CMS-remark: 47730K(51200K)] 48766K(60416K), 0.0001609 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]

[CMS-concurrent-sweep: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[CMS-concurrent-reset: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[GC [ParNew: 9112K->9112K(9216K),0.0000185 secs][CMS: 47728K->51175K(51200K), 0.0184446 secs]56840K->56814K(60416K), [CMS Perm : 2086K->2085K(12288K)], 0.0185255secs] [Times: user=0.02 sys=0.00, real=0.02 secs]

[GC [1 CMS-initial-mark:51175K(51200K)] 56878K(60416K), 0.0000866 secs] [Times: user=0.00 sys=0.00,real=0.00 secs]

[CMS-concurrent-mark: 0.003/0.003secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[CMS-concurrent-preclean:0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[CMS-concurrent-abortable-preclean:0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[GC[YG occupancy: 5703 K (9216K)][Rescan (parallel) , 0.0001108 secs][weak refs processing, 0.0000041 secs][1 CMS-remark: 51175K(51200K)] 56878K(60416K), 0.0001523 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]

[CMS-concurrent-sweep: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[CMS-concurrent-reset: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

[GC [ParNew: 9103K->9103K(9216K),0.0000140 secs][CMS: 51172K->51172K(51200K), 0.0135952 secs]60275K->60269K(60416K), [CMS Perm : 2085K->2084K(12288K)], 0.0136662secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

[Full GC [CMS:51172K->51172K(51200K), 0.0041339 secs] 60269K->60269K(60416K), [CMS Perm: 2084K->2084K(12288K)], 0.0041729 secs] [Times: user=0.01 sys=0.00,real=0.01 secs]

[GC[1 CMS-initial-mark: 51172K(51200K)] 60269K(60416K), 0.0000883 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]

7、CMS收集器的运作步骤

CMS收集器是基于“标记-清除”算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为6个步骤,包括:

初始标记(CMS initial mark)

并发标记(CMS concurrent mark)

并发预清理(CMS-concurrent-preclean)

重新标记(CMS remark)

并发清除(CMS concurrent sweep)

并发重置(CMS-concurrent-reset)

其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC
Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。其他动作都是并发的。

对应具体的运行结果:

[GC [ParNew: 9110K->972K(9216K),0.0056278 secs] 48708K->48702K(60416K), 0.0056664 secs] [Times: user=0.00sys=0.00, real=0.01 secs]

【1.初始标记】

[GC [1 CMS-initial-mark:47730K(51200K)] 48766K(60416K), 0.0000829 secs] [Times: user=0.00 sys=0.00,real=0.00 secs]

【2.并发标记】

[CMS-concurrent-mark: 0.003/0.003secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

【3.并发预清理】

[CMS-concurrent-preclean:0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

【4.重新标记】

[GC[YG occupancy: 1036 K (9216K)][Rescan (parallel) , 0.0001092 secs][weak refs processing, 0.0000140 secs][1 CMS-remark: 47730K(51200K)] 48766K(60416K), 0.0001609 secs] [Times:user=0.00 sys=0.00, real=0.00 secs]

【5.并发清除】

[CMS-concurrent-sweep: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

【6.并发重置】

[CMS-concurrent-reset: 0.000/0.000secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

时间: 2024-08-29 02:00:21

JAVA垃圾收集器之CMS收集器的相关文章

JAVA垃圾收集器之Serial收集器

1.特点 Serial收集器是JAVA虚拟机中最基本.历史最悠久的收集器,在JDK 1.3.1之前是JAVA虚拟机新生代收集的唯一选择.Serial收集器是一个单线程的收集器,但它的"单线程"的意义并不仅仅是说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束. 要是服务器每运行一个小时就会暂停5分钟,老板会有什么样的心情? 2.发展 从JDK 1.3开始,一直到现在还没正式发布的JDK 1.7,HotSpo

JAVA垃圾收集器之ParNew收集器

1.特点 ParNew收集器是JAVA虚拟机中垃圾收集器的一种.它是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio.-XX:PretenureSizeThreshold.-XX:HandlePromotionFailure等).收集算法.Stop The World.对象分配规则.回收策略等都与Serial收集器一致. 2.现状 ParNew是许多运行在Server模式下的虚拟机中首选的

JAVA垃圾收集器之Serial Old收集器

Serial Old收集器是JAVA虚拟机中垃圾收集器的一种,它是Serial收集器的老年代版本,它同样是一个单线程收集器,使用"标记-整理"算法.这个收集器的主要也是在目前的JAVA的Client模式下的虚拟机使用.如果在Server模式下,它主要还有两大用途:一个是在JDK 1.5及之前的版本中与Parallel Scavenge收集器搭配使用,另外一个就是作为CMS收集器的后备预案.如果CMS收集器出现Concurrent Mode Failure,则Serial Old收集器将

Java垃圾收集器之--Garbage-First Collector

简介       Garbage-First(G1)垃圾收集器全面支持JDK7 Upate 4及后续版本.G1收集器是一个服务器形式(server-style)的垃圾收集器,主要用于内存大.多处理器的机器.当您想实现:与应用程序线程并发的执行全局标记,并且有高吞吐量.面向整个堆操作时,它可以满足您高可用性及GC暂停时间的要求. 此举可有效防止中断(GC时的暂停时间)与堆或者实时数据大小成比例增涨. 技术 G1收集器通过如下技术来实现高性能和低暂停时间: 堆被划分成一组相同大小的堆区域(heap

JAVA垃圾收集器之概说

1.垃圾收集器的组合 JAVA垃圾收集器一共有7个,减去还没有正式大规模使用的G1,还有6个,其中新生代3个,老生代3个. 因为垃圾收集器都是一组一组的工作,这6个收集器一共构成了5中使用模式. 参数 描述 -XX:+UseSerialGC Jvm运行在Client模式下的默认值,打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收 -XX:+UseParNewGC 打开此开关后,使用ParNew + Serial Old的收集器进行垃圾回收 -XX:+UseConc

CMS收集器和G1收集器

CMS收集器 CMS收集器是一种以获取最短回收停顿时间为目标的收集器.基于"标记-清除"算法实现,它的运作过程如下: 初始标记 并发标记 重新标记 并发清除 初始标记.从新标记这两个步骤仍然需要"stop the world",初始标记仅仅只是标记一下GC Roots能直接关联到的对象,熟读很快,并发标记阶段就是进行GC Roots Tracing,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生表动的那一部分对象的标记记录,这个阶段的停顿时间

JVM快速调优手册之五: ParNew收集器+CMS收集器的产品案例分析(响应时间优先)

服务器 双核,4个cores; 16G memory [[email protected] ~]# cat /proc/cpuinfo | grep "cpu cores" cpu cores : 2 cpu cores : 2 公式简述 响应时间优先的并发收集器,主要是保证系统的响应时间,减少垃圾收集时的停顿时间.适用于应用服务器.电信领域等. ParNew收集器 ParNew收集器是Serial收集器的多线程版本,许多运行在Server模式下的虚拟机中首选的新生代收集器,除Seri

JAVA垃圾收集器之Parallel Old收集器

Parallel Old收集器是JAVA虚拟机中垃圾收集器的一种.和Serial Old收集器一样,工作在JAV虚拟机的老年代.这种垃圾收集器使用多线程和"标记-整理"算法.它在JDK 1.6中才开始提供. 在注重吞吐量及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器. 1.运行代码 package com.gc; import java.util.ArrayList; import java.util.List; /** * 简

JAVA垃圾收集器之Parallel Scavenge收集器

Parallel Scavenge收集器是JAVA虚拟机中垃圾收集器的一种.和ParNew收集器类似,是一个新生代收集器.使用复制算法的并行多线程收集器. 1.特点 Parallel Scavenge收集器的关注点与其他收集器不同, ParallelScavenge收集器的目标则是达到一个可控制的吞吐量(Throughput).所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,