[gc] GC调优及awk脚本分析GC日志

阅读更多
原有GC参数
JAVA_OPTS="-server -XX:+UseParNewGC -Xms768m -Xmx1280m -XX:MaxNewSize=128m -XX:NewSize=128m -XX:PermSize=96m -XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:CMSInitiatingOccupancyFraction=1 -XX:+CMSIncrementalMode -XX:MaxTenuringThreshold=0 -XX:SurvivorRatio=20000 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0  -XX:CMSIncrementalDutyCycleMin=10 -XX:CMSIncrementalDutyCycle=30 -XX:CMSMarkStackSize=8M -XX:CMSMarkStackSizeMax=32M"
调优后的GC参数
JAVA_OPTS="-server -XX:+UseParNewGC -Xms768m -Xmx1280m -XX:MaxNewSize=128m -XX:NewSize=128m -XX:PermSize=96m -XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0  -XX:CMSMarkStackSize=8M -XX:CMSMarkStackSizeMax=32M"

原有现象:

每天有大量的CMS (concurrent mode failure)导致的Full GC.
根据网上相关文档,CMS (concurrent mode failure)的原因为当对年轻代GC时,vm估算年老代空间不足或者由于年老代内存碎片,年老代无法容纳最坏情况下的对象提升(把对象从年轻代移到年老代)而触发。
CMS GC被中断,使用Serial Collector进行Full GC。
一般的处理方式为增大年老代空间,减少年老代内存碎片。

由于原有参数中有-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0,每次都会做compaction,所以推断不是内存碎片的原因。

但是观测GC日志
stdout.log.20110711.142555:26031.261: [Full GC 26031.262: [CMS (concurrent mode failure): 96426K->96441K(655360K), 0.7360040 secs] 182006K->96441K(786368K), [CMS Perm : 52050K->52028K(98304K)], 0.7362900 secs]
发现还有大量的年老代内存未使用。


同时观测到原有配置下CMS GC比较多。
原因为参数配置了 -XX:SurvivorRatio=20000,导致Survivor基本没有被使用,大量短生命对象会被移动到年老代,导致CMS GC比较多。

原来配置的策略是想让对象尽快移动到年老代,使用了iCMS GC,试图尽量降低GC对APP的影响,缩短响应时间。
但是结果导致CMS GC频繁触发。
1670.018: [GC [1 CMS-initial-mark: 92481K(655360K)] 138440K(786368K), 0.0881390 secs]
可以看到,年老代的内存还有大量空闲的时候就会触发CMS GC。

原有-XX:CMSInitiatingOccupancyFraction=1配置基本没有用。
标准CMS可以由以下2个事件触发:
1 vm经过计算认为可以触发了。
2 或者年老代空间占用比超过了CMSInitiatingOccupancyFraction。默认68%。

想让vm只被内存占用比超限触发,可以同时配置
-XX:CMSInitiatingOccupancyFraction=xx和UseCMSInitiatingOccupancyOnly选项。

同时,没有找到iCMS如何决定触发gc的时间。

根据web app的特点,短生命周期对象比较多,应该尽量让对象被minor gc回收掉。一般而言,minor gc的速度还是很快的。
综上所述,调整了GC参数,删除了iCMS的配置,调整SurvivorRatio使用默认值25。

以下为调整前后数据对比(单位为天,空为0):
actioin = concurrent mode failure Unloading class count = 12
actioin = concurrent mode failure Unloading class count =

actioin = concurrent mode failure count = 168 time = 123.321 total_time = 493.283
actioin = concurrent mode failure count =  time =  total_time = 0

actioin = Full GC CMS-concurrent-mark count = 145 time = 64.492 total_time = 64.492
actioin = Full GC CMS-concurrent-mark count =  time =  total_time =

actioin = Full GC Unloading.class count = 10
actioin = Full GC Unloading.class count =

actioin = Full GC count = 156 time = 105.449 total_time = 421.796
actioin = Full GC count = 24 time = 17.0339 total_time = 68.1356

actioin = CMS-initial-mark count = 1560 time = 120.12 total_time = 480.479
actioin = CMS-initial-mark count =  time =  total_time = 0

actioin = Young GC count = 2383 time = 22.6272 total_time = 90.5086
actioin = Young GC count = 2766 time = 22.0372 total_time = 88.1487

可以看到:

GC动作concurrent mode failure Unloading class,原有每天12次左右,调整后为0.

原有concurrent mode failure导致full gc每天168次左右,耗时123.321m,调整后为0.

Full GC CMS-concurrent-mark该动作没有找到确切含义,但是也是从每天145次降到0.

Full GC Unloading.class从每天10次降为0.

Full GC从每天156次,降为24次,时间从105.449m,降到17.0339s。
单次Full GC的时间峰值没有变长。

CMS GC从每天1560,降为0,。

minor GC次数略有增大,但是时间每次minor时间减少,总时间基本保持不变。

总而言之,调整的效果还是比较好的。

经验小结:
搞清楚gc的机制。
搞不清楚的时候动手实验。
只有搞清楚了gc参数的意义并且验证了结果才配置特殊的gc参数。
写脚本分析gc日志来对比调整前后的gc运行情况。

awk脚本如下:
note:
1 由于服务器不支持“语句;语句”的形式,改为{{语句}{语句}}的形式。
2 由于服务器不支持for(init;condition;increment){语句}的形式,改为对应的while形式。


--统计gc信息 单机版本
#本脚本监测GC日志,输出gc动作的次数,耗时。

##start

grep [0-9][0-9][0-9]: stdout.log |
 awk 
 '
BEGIN
{

 #常量定义
{secondsInHour=3600}
{secondsInDay=24*3600}
{zero=0}


 #用户输入变量 机器核数
 {cores=4}
 
 #不需要计算的,这些action都有对应的带有时间的日志输出。
 #33.039: [CMS-concurrent-mark-start]
 #33.261: [CMS-concurrent-preclean-start]
 #33.270: [CMS-concurrent-abortable-preclean-start]
 #37.094: [CMS-concurrent-sweep-start]
 #37.205: [CMS-concurrent-reset-start]
 {ignoreList["CMS-concurrent-mark-start"]=0}
 {ignoreList["CMS-concurrent-preclean-start"]=0}
 {ignoreList["CMS-concurrent-abortable-preclean-start"]=0}
 {ignoreList["CMS-concurrent-sweep-start"]=0}
 {ignoreList["CMS-concurrent-reset-start"]=0}
 
 
                
 #number规定连续的优先级,number越小,优先级越高。
 #以下每个number相同的项,定义了一个gc action。
 
 #gc日志的关键字。可以使用正则表达式。
 #{searchKey[number]="concurrent mode failure"}
 
 #输出中的文字。
 #{msg[number]="concurrent mode failure"}
 
 #该动作是否有耗时,1为有,0为无。
 #{hastime[number]=1}
 
 #查找时间时日志的分隔符。
 #{timesperator[number]="[ ]"}
 
 #耗时所在的序位数,从1开始。
 #{timeindex[number]=10}
 
 #gc是否是并发动作,1为和app并发,0为停止app,只能执行gc.
 #{currentactions[number]=0}
 
 
 
 
 # 没有什么好说的,unloading class。
 {searchKey[1]="concurrent mode failure.*Unloading class"}
 {msg[1]="concurrent mode failure Unloading class"}
 {hastime[1]=0}
 
 # concurrent mode failure
 # The concurrent mode failure can either be avoided by increasing the tenured generation size or initiating the CMS collection at a lesser heap occupancy
 {searchKey[2]="concurrent mode failure"}
 {msg[2]="concurrent mode failure"}
 {hastime[2]=1}
 {timesperator[2]="[ ]"}
 {timeindex[2]=10}
 {currentactions[2]=0}
  
 
 #38182.941: [Full GC 38182.942: [CMS38183.331: [CMS-concurrent-mark: 0.390/60.394 secs] 
 {searchKey[3]="Full.GC.*CMS-concurrent-mark"}
 {msg[3]="Full GC CMS-concurrent-mark"}
 {hastime[3]=1}
 {timesperator[3]="[ /]"}
 {timeindex[3]=7}
 
 #存疑 ???????????????????????????????????????????????????????????????
 #不清楚该项是否为并发。暂定为不是并发。
 {currentactions[3]=0}
 
 
 
 #unloading class
 #202826.209: [Full GC 202826.209: [CMS[Unloading class sun.reflect.GeneratedMethodAccessor1455] 
 {searchKey[4]="Full.GC.*Unloading.class"}
 {msg[4]="Full GC Unloading.class"}
 {hastime[4]=0}
 
 
 
 
 #31312.911: [Full GC 31312.911: [CMS: 95681K->95673K(655360K), 0.6410280 secs] 132804K->95673K(786368K), [CMS Perm : 51969K->51959K(98304K)], 0.6412890 secs]
 {searchKey[5]="Full.GC"}
 {msg[5]="Full GC"}
 {hastime[5]=1}
 {timesperator[5]="[ ]"}
 {timeindex[5]=7}
 {currentactions[5]=0}
 
 
 
 #-------------------------------------------------------
 # CMS phase.
 #-------------------------------------------------------
  
 # CMS initial mark
 #167885.626: [GC [1 CMS-initial-mark: 106656K(655360K)] 152632K(786368K), 0.0822730 secs]
 {searchKey[6]="GC.*CMS-initial-mark"}
 {msg[6]="CMS-initial-mark"}
 {hastime[6]=1}
 {timesperator[6]="[ ]"}
 {timeindex[6]=7}
 {currentactions[6]=0}
 
 # CMS concurrent mark
 #0.334: [CMS-concurrent-mark: 0.077/0.079 secs]
 {searchKey[7]="CMS-concurrent-mark:"}
 {msg[7]="CMS-concurrent-mark"}
 {hastime[7]=1}
 {timesperator[7]="[ /]"}
 {timeindex[7]=4}
 {currentactions[7]=1}
 
 # CMS CMS-concurrent-preclean
 #170972.192: [CMS-concurrent-preclean: 0.000/0.000 secs]
 {searchKey[8]="CMS-concurrent-preclean:"}
 {msg[8]="CMS-concurrent-preclean"}
 {hastime[8]=1}
 {timesperator[8]="[ /]"}
 {timeindex[8]=4}
 {currentactions[8]=1}
 
 # CMS-concurrent-abortable-preclean
 #60.393: [CMS-concurrent-abortable-preclean: 0.000/0.000 secs]
 {searchKey[9]="CMS-concurrent-abortable-preclean:"}
 {msg[9]="CMS-concurrent-abortable-preclean"}
 {hastime[9]=1}
 {timesperator[9]="[ /]"}
 {timeindex[9]=4}
 {currentactions[9]=1}
 
 #  CMS-remark
 #1040.645: [GC[YG occupancy: 67535 K (131008 K)]1040.645: [Rescan (parallel) , 0.0682420 secs]1040.713: [weak refs processing, 0.0219150 secs] [1 CMS-remark: 95660K(655360K)] 163195K(786368K), 0.0903850 secs]
 {searchKey[10]="CMS-remark"}
 {msg[10]="CMS-remark"}
 {hastime[10]=1}
 {timesperator[10]="[ ]"}
 {timeindex[10]=22}
 {currentactions[10]=0}
 
 # CMS-concurrent-sweep
 #42.720: [CMS-concurrent-sweep: 0.052/0.890 secs]
 {searchKey[11]="CMS-concurrent-sweep:"}
 {msg[11]="CMS-concurrent-sweep"}
 {hastime[11]=1}
 {timesperator[11]="[ /]"}
 {timeindex[11]=4}
 {currentactions[11]=1}
 
 
 # CMS-concurrent-reset
 #214.712: [CMS-concurrent-reset: 0.013/0.013 secs]
 {searchKey[12]="CMS-concurrent-reset:"}
 {msg[12]="CMS-concurrent-reset"}
 {hastime[12]=1}
 {timesperator[12]="[ /]"}
 {timeindex[12]=4}
 {currentactions[12]=1}
 
 #-------------------------------------------------------
 # CMS phase end.
 #-------------------------------------------------------
 
 
 # Young GC
 #11135.036: [GC 11135.036: [ParNew: 130944K->0K(131008K), 0.0112680 secs] 233110K->103480K(786368K), 0.0114720 secs]
 {searchKey[13]="GC.*ParNew"}
 {msg[13]="Young GC"}
 {hastime[13]=1}
 {timesperator[13]="[ ]"}
 {timeindex[13]=9}
 {currentactions[13]=0}
 
 
 # 计算要计数的gc action。
 {maxPriority=0}
 
 for(key in searchKey) { maxPriority++ }
 
 
 #变量,最大日期。
 {maxDay=0} 
 
#end of BEGIN 
}


{

# .的位置
{dotIndex=index($0,".")}
# 总秒数
{seconds=zero+substr($0,1,dotIndex)}

# 第几天,index from 0.
{leftDaySeconds=seconds%secondsInDay}
{day=(seconds-leftDaySeconds)/secondsInDay}


# 第几个小时,index from 0.
{leftHourSeconds=leftDaySeconds%secondsInHour}
{hour=(leftDaySeconds-leftHourSeconds)/secondsInHour}

# 存储最大日期。
{maxDay=day}



{temp=1}
while(temp<=maxPriority)
{
  if(match($0,searchKey[temp])>0)
  {

    {gcday[day , msg[temp]]++}
    {gchour[day,hour,msg[temp]]++} 
    
    if(hastime[temp]==1)
    {
     {split($0,fields,timesperator[temp])} 
     {gcdaytime[day,msg[temp]]+=fields[timeindex[temp]]} 
     {gchourtime[day,msg[temp]]+=fields[timeindex[temp]]}
    }
    
    {next}
  }

  {temp++}
}


#忽略指定的gc log
for(ignoreAction in ignoreList)
{
if(index($0,ignoreAction)>0){next}
}

{print $0}

}


END
{

  {temp=1}
  while(temp<=maxPriority)
  {  
    {print "\n\n"}  
    
    {temDay=maxDay}    
    while(temDay>=0)
    {
      
      {timeString=""}      
      {totalTimeString=""}
      
      if(hastime[temp]==1)
      {
      {timeString=" time = " gcdaytime[temDay,msg[temp]]}
      
      
      if(currentactions[temp]==0)
      {totalTimeString=" total_time = " gcdaytime[temDay,msg[temp]]*cores}
      else
      {totalTimeString=" total_time = " gcdaytime[temDay,msg[temp]]}      
      
      }     
      
      
      {print "actioin = " msg[temp] " count = " gcday[temDay,msg[temp]] timeString totalTimeString}
      
      
      {temDay--}        
    }        
    
    
    {temp++}    
  }    
  
}

 '
| less

##end









你可能感兴趣的:([gc] GC调优及awk脚本分析GC日志)