28 October 2013

选择高性能收集器有很多原因。应用程序可能会从较短的GC暂停中受益,并且也愿意运行 更多线程(占用CPU资源)来加快速度。或者你想控制GC暂停的频度。除了基本的收集器,你 还可以用选项迫使平台采用不同的收集策略。 接下来的这两个收集器就是为了满足这样的情况开发的。

并发标记清除

并发标记清除(CMS)收集器是Java 5推荐的高性能收集器,在Java 6中仍然保持了旺盛的生命力。可以通过下面几个选项激活它.

选项 效果
-XX:+UseConcMarkSweepGC 打开CMS收集
-XX:+CMSIncrementalMode 增量模式(一般都需要)
XX:+CMSIncrementalPacing 配合增量模式,根据应用程序的行为自动调整每次执行的 垃圾回收任务的幅度(一般都需要)
-XX:+UseParNewGC 并发收集年轻代
-XX:ParallelGCThreads=<N> GC使用的线程数

下面是与标记清除相关的三个重要事实:

1.某种世界停转(简称STW)的暂停是不可避免的; 2.GC子系统绝对不能漏掉存活对象,这样做会导致JVM垮掉(或者更糟); 3.只有所有应用线程都为整体收集暂停下来,才能保证收集所有的垃圾。

CMS利用了最后一点。它制造两个非常短暂的STW暂停,并且在GC周期的剩余时间和应用 程序的线程一起运行。这表明它愿意跟“伪阴性”妥协,由于竞争危害而无法标识某些垃圾(被 漏掉的垃圾会在下一个GC周期中得到收集)。
CMS还要在运行时做复杂的记账工作,记录哪些是垃圾,哪些不是。这些额外的开销是为了 在不停止应用线程的情况下运行GC所付出的代价。CMS在有更多CPU核心的机器上会表现得更 好,并且会制造更频繁的短暂暂停。它的日志输出如下所示:

2013-10-28T15:32:50.698+0800: [GC [1 CMS-initial-mark: 61186K(707840K)] 221619K(1014528K), 0.1101580 secs] [Times: user=0.11 sys=0.00, real=0.11 secs]
2013-10-28T15:32:50.955+0800: [CMS-concurrent-mark: 0.147/0.147 secs] [Times: user=0.24 sys=0.00, real=0.15 secs]
2013-10-28T15:32:50.962+0800: [CMS-concurrent-preclean: 0.006/0.006 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2013-10-28T15:32:51.780+0800: [GC [ParNew: 306687K->34048K(306688K), 0.4628720 secs] 367873K->110235K(1014528K) icms_dc=16 , 0.4629370 secs] [Times: user=0.13 sys=0.15, real=0.47 secs]

这些日志和基本的GC日志差不多,但增加了CMS和CMS Perm收集器部分。

新的收集器:G1

G1是Java平台中崭新的收集器。本来想把它和Java 7一起发布,但后来作为预发布版本跟Java 6一起发布了,到Java 7时就是成品了。它在Java 6中并没有得到广泛的应用,但随着Java 7逐渐普 及,有望让G1成为高性能应用(也可能是所有应用)的默认选择。
G1的核心思想是暂停目标(pause goal),也就是程序在执行时能为GC暂停多长时间(比如 每5分钟20ms)。G1会竭尽所能达成暂停目标。它和我们原来遇到的收集器完全不同,并且开发 人员对GC如何执行有更多控制权。
G1不是真正的分代式垃圾收集器(尽管它仍然使用标记清除法)。相反,G1把堆分成大小相 同的区域(比如每个1 MB),不区分年轻区和年老区。暂停时,对象被撤到其他区域(就像伊甸 园对象被挪到幸存者乐园一样),清空的区域被放回到(空白区的)自由列表上。这种将堆划分 为大小相同区域的做法如下图所示:

G1如何划分堆空间

这个新的收集策略让Java平台可以统计收集单个区域需用的平均时长。这样你就可以在合理 范围内指定一个暂停目标。G1只会在有限的时间内收集尽可能多的区域(尽管在收集最后一个 区域时所用的时间可能比预期的长)。
要打开G1,需要用到下表中的选项。

选项 效果
-XX:+UseG1GC 打开G1收集
-XX:MaxGCPauseMillis=50 告诉G1它在一次收集中暂停的时间应该尽量保持在50ms以内
-XX:GCPauseIntervalMillis=200 告诉G1它将两次收集的时间间隔尽量保持在200ms以上

GC 系统所能承受的压力是有限的。必须有充足的暂停时间把垃圾取出来。每隔100年1ms的暂停目标肯定是不现实的。
G1可以支持的负载和应用类型范围很广。如果你的应用程序已经到了需要对GC调优的地步, G1会是一个不错的选择。



blog comments powered by Disqus