GC策略的调优

摘自《构建高性能的大型分布式Java应用》第六章,感兴趣的同学们可以看看。

GC策略在G1还没成熟的情况下,目前主要有串行、并行和并发三种,对于大内存的应用而言,串行的性能太低,因此使用到的主要是并行和并发两种,具体这两种GC的策略在深入JVM章节中已讲解, 并行和并发GC的策略通过-XX:+UseParallelGC-XX:+UseConcMarkSweepGC来指定,还有一些细节的配置参数用来配置策略的执行方式,例如:-XX:ParallelGCThreads-XX:CMSInitiatingOccupancyFraction等,新生代对象回收只可选择并行,在此就举例来看看两种GC策略在Full GC时的具体表现状况。

测试GC策略状况的代码如下:

public class GCPolicyDemo {
 
    
/**
     * 
@param args
     
*/
    
public static void main(String[] args) throws Exception{
       System.out.println(
"ready to start");
       Thread.sleep(
10000);
       List
<GCPolicyDataObject> cacheObjects=new ArrayList<GCPolicyDataObject>();
       
for (int i = 0; i < 2048; i++) {
           cacheObjects.add(
new GCPolicyDataObject(100));
       }
       System.gc();
       Thread.sleep(
1000);
       
for (int i = 0; i < 10; i++) {
           System.out.println(
"Round: "+(i+1));
           
for (int j = 0; j < 5; j++) {
              System.out.println(
"put 64M objects");
              List
<GCPolicyDataObject> tmpObjects=new ArrayList<GCPolicyDataObject>();
              
for (int m = 0; m < 1024; m++) {
                  tmpObjects.add(
new GCPolicyDataObject(64));
              }
              tmpObjects
=null;
           }
       }
       cacheObjects.size();
       cacheObjects
=null;
    }
 
}
 
class GCPolicyDataObject{
   
    
byte[] bytes=null;
   
    GCPolicyRefObject object
=null;
   
    
public GCPolicyDataObject(int factor){
       bytes
=new byte[factor*1024];
       object
=new GCPolicyRefObject();
    }
   
}
 
class GCPolicyRefObject{
   
    GCPolicyRefChildObject object;
   
    
public GCPolicyRefObject(){
       object
=new GCPolicyRefChildObject();
    }
   
}
 
class GCPolicyRefChildObject{
   
    
public GCPolicyRefChildObject(){
       ;
    }
   
}

 

-Xms680M -Xmx680M -Xmn80M -XX:+UseConcMarkSweepGC -XX:+PrintGCApplicationStoppedTime -XX:+UseCMSCompactAtFullCollection -XX:+UseParNewGC -XX:CMSMaxAbortablePrecleanTime=5参数执行以上代码,通过jstat观察到的GC状况如下:

共触发39minor GC,耗时为1.197秒,共触发21Full GC,耗时为0.136秒,GC总耗时为1.333秒。

GC动作造成应用暂停的时间为:1.74秒。

-Xms680M -Xmx680M -Xmn80M -XX:+PrintGCApplicationStoppedTime –XX:+UseParallelGC参数执行以上代码,通过jstat观察到的GC状况如下:

共触发119minor GC,耗时为2.774秒,共触发8Full GC,耗时为0.243秒,GC总耗时为3.016秒。

GC动作造成应用暂停的时间为:3.11秒。

从上面的结果来看,由于CMS GC多数动作是和应用并发做的,采用CMS GC确实可以减小GC动作给应用造成的暂停,但也正因为是并发进行的,因此CMS GC需要耗费更多的CPU,因此对于CPU密集型应用而言,CMS不一定是好的选择。

在采用CMS GC的情况下,尤其要注意的是concurrent mode failure的现象,这可以通过-XX:+PrintGCDetails来观察,当出现concurrent mode failure的现象时,就意味着此时JVM将继续采用Stop-The-World的方式来进行Full GC,这种情况下,采用CMS就没什么意义了,造成concurrent mode failure的原因主要是当minor GC进行时,旧生代所剩下的空间小于Eden区域+From区域的空间,要避免这种现象,可以采用以下三种方法:

l  调低触发CMS GC执行的阀值

CMS GC触发主要由CMSInitiatingOccupancyFraction值决定,默认情况是当旧生代已用空间为68%时,即触发CMS GC

在出现concurrent mode failure的情况下,可考虑调小这个值,提前CMS GC的触发,以保证旧生代有足够的空间。

l  扩大旧生代空间

调小新生代占用的空间或增大整个JVM Heap的空间可扩大旧生代空间,这对于避免concurrent mode failure现象可以提供很大的帮助。

l  调小CMSMaxAbortablePrecleanTime的值

CMS GC需要经过较多步骤才能完成一次GC的动作,在minor GC较为频繁的情况下,很有可能造成CMS GC尚未完成,从而造成concurrent mode failure,这种情况下,减少minor GC触发的频率是一种方法,另外一种方法则是加快CMS GC执行时间,在CMS的整个步骤中,JDK 5.0+6.0+的有些版本在CMS-concurrent-abortable-preclean-startCMS-concurrent-abortable-preclean这两步间有可能会耗费很长的时间,导致可回收的旧生代的对象很长时间后才被回收,这是Sun JDK CMS GC的一个bug[1],如通过PrintGCDetails观察到这两步之间耗费了较长的时间,可以通过-XX: CMSMaxAbortablePrecleanTime设置较小的值,以保证CMS GC尽快完成对象的回收,避免concurrent mode failure的现象。


[1] 详细bug信息请见:http://www.nabble.com/CMS-GC-tuning-under-JVM-5.0-td16759819.html

posted on 2009-10-09 15:57 BlueDavy 阅读(14445) 评论(5)  编辑  收藏 所属分类: Java

评论

# re: GC策略的调优 2009-10-09 16:36 agraviton

bluedavy 又出手了,出手就不凡。  回复  更多评论   

# re: GC策略的调优 2009-10-09 18:06 glf

书什么时候出啊  回复  更多评论   

# re: GC策略的调优 2009-10-09 20:10 BlueDavy

@glf
这书估计要到明年三月左右才能上市了。  回复  更多评论   

# re: GC策略的调优 2009-10-10 13:54 hucq

期待实体书  回复  更多评论   

# re: GC策略的调优 2009-12-21 21:16 study

太期待了!   回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

公告

 









feedsky
抓虾
google reader
鲜果

导航

<2009年10月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

统计

随笔分类

随笔档案

文章档案

Blogger's

搜索

最新评论

阅读排行榜

评论排行榜