内存频繁GC问题查找分享

一、基础知识

1.      Sun JVM内存基本结构:


其中,比较重要的是Permanent Space(方法区),Java Object Heap(堆区)

      Heap Space又可分为YoungGeneration(年轻代)和Old Generation(年老代)

      而年轻代又可分为EdenSpace和Survivor Space

2.      对象可及性判断:

目前定义了几个root,也就是这几个对象是jvm虚拟机不会被回收的对象,所以这些对象引用的对象都是在使用中的对象,
这些对象未使用的对象就是即将要被回收的对象。简单就是说:如果对象能够达到root,就不会被回收,如果对象不能够达到root,就会被回收。
如下图:对象D访问不到根对象,所以就会被回收



以下对象会被认为是root对象:

被启动类(bootstrap加载器)加载的类和创建的对象
jvm运行时方法区类静态变量(static)引用的对象
jvm运行时方法去常量池引用的对象
jvm当前运行线程中的虚拟机栈变量表引用的对象
本地方法栈中(jni)引用的对象

由于这种算法即使存在互相引用的对象,但如果这两个对象无法访问到根对象,还是会被回收。如下图:对象C和对象D互相引用,但是由于无法访问根,所以会被回收。



jvm在确定是否回收的对象的时候采用的是root搜索算法来实现。

 

3.      不同代的垃圾回收算法

说明:对于FULL GC,本文中的定义是指GC日志中出现full gc字样的垃圾收集,或者serial old收集(CMS并行收集出现Concurrent Mode Failure会退化为serial old)时的垃圾收集;

     Dragoon监控中的full gc次数是指年老代的回收次数,即本文中的FULL GCCMS收集;

Eden Space用于存放新生成的对象,经过一次垃圾回收后会进入Survivor Space;

Survivor Space分为均等的两块,每次垃圾回收会将有用的对象从一块移到另一块(停止复制算法/ minor GC),经过指定次数的回收后(由MaxTenuringThreshold参数确定,当指定-XX:+UseAdaptiveSizePolicy后,会取Survivor中超过一半对象的age和MaxTenuringThreshold的最小值);

Tenured Space则执行标记清扫回收算法(CMS收集策略,软引用对象不会回收),如果出现Concurrent Mode Failure,则进入full GC(软引用对象会被回收);

 

 

4.      不同代大小的分配参数

方法区分配参数: -XX:MaxPermSize

堆区分配参数: -Xms,-Xmx

年轻代分配参数: -XX:NewRatio 设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5

               -Xmn:设置年轻代大小;

Eden与Survivor大小参数: -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

 

二、问题查找

5.      Scout新机房生产环境启动参数示例:


参数含义:

-XX:MaxPermSize=256 方法区大小为256M

-Xms3071m –Xmx3071m 堆区大小为3071m

-Xmn1151m 堆区的年轻代大小为1151m

-DisableExplicitGC  System.gc()无效

-XX:SurvivorRatio=6 两个survivor和一个Eden区的比值为2:6;

-XX:MaxTenuringThreshold=5 在survivor代复制5次后的对象进入年老代;

-XX:CMSInitiatingOccupancyFraction=70 年老代内存占用超过70%时进行垃圾回收

-XX:+UseConcMarksweepGC 使用并发收集器

-XX:+UseParNewGC设置年轻代为并行收集

-XX:+UseCMSCompactAtFullCollection  FullGC时进行内存压缩(内存碎片整理)

-XX:+CMSFullGCsBeforeCompaction=5  执行5次FULL GC后对内存压缩  默认是0

-XX:+UseCMSInitiatingOccupancyOnly 指示只有在oldgeneration 在使用了初始化的比例后concurrent collector启动收集

 

Scout老机房生产环境启动参数示例:


     老机房与新机房启动参数中最大的差别在于老机房未指定年轻代的大小;

      

6.      GC日志解读:

(1):2013-08-26T22:02:03.472+0800: 29733.146: [GC 29733.146:[ParNew: 66342K->2586K(74560K), 0.0103010 secs]354578K->290937K(2242176K), 0.0105390 secs] [Times: user=0.02 sys=0.00,real=0.01 secs]

   该日志表明这是一个年轻代的垃圾收集,其中年轻代总大小为74560K,经过垃圾收集后年轻代对象大小由66342K降为2586K; 堆区内存总大小为2242176K,经垃圾收集后,堆中对象大小由354578K降为290937K;

(2): 2013-08-26T22:03:30.155+0800: 29819.829: [GC [1CMS-initial-mark: 1562145K(2167616K)] 1574097K(2242176K), 0.0406330 secs][Times: user=0.04 sys=0.00, real=0.03 secs]

   该日志表明这是年老代CMS垃圾收集(标记清理),其中年老代总大小为2167616K,其中由root直接可达的对象大小为 1562145K;

 

7.      Scout生产环境日志:

1.      Scout老机房GC日志:

2013-08-26T22:02:03.472+0800: 29733.146: [GC 29733.146: [ParNew:66342K->2586K(74560K), 0.0103010 secs] 354578K->290937K(2242176K),0.0105390 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]

2013-08-26T22:02:07.509+0800: 29737.182: [GC 29737.182:[ParNew: 66522K->10624K(74560K), 0.1055890 secs]354873K->309226K(2242176K), 0.1059260 secs] [Times: user=0.23 sys=0.00,real=0.10 secs]

……300行

2013-08-26T22:03:29.520+0800:29819.194: [GC 29819.194: [ParNew: 74560K->10624K(74560K), 0.1820460 secs]1546399K->1527609K(2242176K), 0.1823400 secs] [Times: user=0.59 sys=0.05,real=0.18 secs]

2013-08-26T22:03:29.960+0800:29819.633: [GC 29819.633: [ParNew: 74560K->10624K(74560K), 0.1915000 secs]1591545K->1572769K(2242176K), 0.1917850 secs] [Times: user=0.56 sys=0.03,real=0.19 secs]

2013-08-26T22:03:30.155+0800: 29819.829: [GC [1CMS-initial-mark: 1562145K(2167616K)] 1574097K(2242176K), 0.0406330 secs][Times: user=0.04 sys=0.00, real=0.03 secs]

2013-08-26T22:03:30.196+0800:29819.870: [CMS-concurrent-mark-start]

2013-08-26T22:03:30.790+0800:29820.464: [GC 29820.464: [ParNew: 74560K->10624K(74560K), 0.1747140 secs]1636705K->1625458K(2242176K), 0.1750200 secs] [Times: user=0.55 sys=0.00,real=0.17 secs]

2013-08-26T22:03:33.176+0800:29822.849: [CMS-concurrent-mark: 2.789/2.980 secs] [Times: user=6.89 sys=0.13,real=2.99 secs]

2013-08-26T22:03:33.176+0800:29822.849: [CMS-concurrent-preclean-start]

2013-08-26T22:03:33.345+0800:29823.018: [CMS-concurrent-preclean: 0.117/0.169 secs] [Times: user=0.28sys=0.06, real=0.16 secs]

2013-08-26T22:03:33.345+0800:29823.018: [CMS-concurrent-abortable-preclean-start]

2013-08-26T22:03:33.839+0800:29823.512: [GC 29823.512: [ParNew: 74560K->10624K(74560K), 0.1377600 secs]1689394K->1687128K(2242176K), 0.1380420 secs] [Times: user=0.42 sys=0.03,real=0.14 secs]

2013-08-26T22:03:36.341+0800:29826.014: [CMS-concurrent-abortable-preclean: 2.768/2.996 secs] [Times:user=6.08 sys=0.19, real=3.00 secs]

2013-08-26T22:03:36.360+0800:29826.034: [GC[YG occupancy: 43864 K (74560 K)]29826.034: [Rescan (parallel) ,0.0158540 secs]29826.050: [weak refs processing, 0.0090460 secs] [1 CMS-remark:1676504K(2167616K)] 1720369K(2242176K), 0.0258450 secs] [Times: user=0.06sys=0.00, real=0.02 secs]

2013-08-26T22:03:36.391+0800:29826.064: [CMS-concurrent-sweep-start]

2013-08-26T22:03:37.999+0800:29827.673: [CMS-concurrent-sweep: 1.608/1.608 secs] [Times: user=3.23 sys=0.00,real=1.61 secs]

2013-08-26T22:03:37.999+0800:29827.673: [CMS-concurrent-reset-start]

2013-08-26T22:03:38.021+0800:29827.695: [CMS-concurrent-reset: 0.022/0.022 secs] [Times: user=0.02 sys=0.01,real=0.02 secs]

2013-08-26T22:03:39.382+0800: 29829.055: [GC 29829.055:[ParNew: 74560K->10624K(74560K), 0.1359850 secs]1569110K->1564430K(2242176K), 0.1362710 secs] [Times: user=0.42 sys=0.06,real=0.13 secs]

……..

2013-08-26T22:05:49.464+0800:29959.138: [CMS-concurrent-abortable-preclean-start]

 CMS: abort preclean due to time2013-08-26T22:05:54.543+0800: 29964.216: [CMS-concurrent-abortable-preclean:1.241/5.078 secs] [Times: user=1.11 sys=0.00, real=5.08 secs]

2013-08-26T22:05:54.543+0800:29964.217: [GC[YG occupancy: 6683 K (74560 K)]29964.217: [Rescan (parallel) ,0.0056000 secs]29964.223: [weak refs processing, 0.0001400 secs] [1 CMS-remark:1526804K(2167616K)] 1533488K(2242176K), 0.0059040 secs] [Times: user=0.00sys=0.00, real=0.00 secs]

2013-08-26T22:05:54.550+0800:29964.223: [CMS-concurrent-sweep-start]

2013-08-26T22:05:57.258+0800:29966.931: [CMS-concurrent-sweep: 2.708/2.708 secs] [Times: user=2.74 sys=0.02,real=2.71 secs]

2013-08-26T22:05:57.258+0800:29966.931: [CMS-concurrent-reset-start]

2013-08-26T22:05:57.263+0800:29966.937: [CMS-concurrent-reset: 0.006/0.006 secs] [Times: user=0.01 sys=0.00,real=0.01 secs]

2013-08-26T22:06:05.454+0800: 29975.128:[GC 29975.128: [ParNew: 65911K->1918K(74560K), 0.0119430 secs] 210470K->146513K(2242176K), 0.0121880 secs] [Times: user=0.02 sys=0.00,real=0.01 secs]

2.      Scout新机房日志:

   2013-08-31T21:54:01.216+0800: 183388.863:[GC 183388.863: [ParNew: 888447K->4334K(1031296K), 0.0086680 secs]1152716K->268712K(2998400K), 0.0090100 secs] [Times: user=0.04 sys=0.00,real=0.01 secs]

2013-08-31T22:02:09.217+0800:183876.864: [GC 183876.864: [ParNew: 888302K->103323K(1031296K), 0.0712750secs] 1152680K->367965K(2998400K), 0.0716890 secs] [Times: user=0.30sys=0.00, real=0.08 secs]

2013-08-31T22:02:11.049+0800:183878.696: [GC 183878.696: [ParNew: 987291K->147328K(1031296K), 0.4502220secs] 1251933K->538963K(2998400K), 0.4506300 secs] [Times: user=1.36sys=0.00, real=0.45 secs]

2013-08-31T22:02:13.250+0800:183880.897: [GC 183880.897: [ParNew: 1031296K->147328K(1031296K), 0.2101750secs] 1422931K->696288K(2998400K), 0.2105580 secs] [Times: user=0.76 sys=0.00,real=0.21 secs]

2013-08-31T22:02:15.199+0800:183882.846: [GC 183882.846: [ParNew: 1031296K->147328K(1031296K), 0.1986570secs] 1580256K->839485K(2998400K), 0.1990570 secs] [Times: user=0.73sys=0.00, real=0.19 secs]

2013-08-31T22:02:17.184+0800:183884.831: [GC 183884.831: [ParNew: 1031296K->147328K(1031296K), 0.2074570secs] 1723453K->1004694K(2998400K), 0.2078890 secs] [Times: user=0.75sys=0.00, real=0.21 secs]

2013-08-31T22:02:19.168+0800:183886.814: [GC 183886.815: [ParNew: 1031296K->147328K(1031296K), 0.1993270secs] 1888662K->1152084K(2998400K), 0.1997020 secs] [Times: user=0.72sys=0.00, real=0.20 secs]

2013-08-31T22:02:21.136+0800:183888.783: [GC 183888.783: [ParNew: 1031296K->147328K(1031296K), 0.2043320secs] 2036052K->1301202K(2998400K), 0.2047440 secs] [Times: user=0.74sys=0.00, real=0.21 secs]

2013-08-31T22:02:23.090+0800:183890.737: [GC 183890.737: [ParNew: 1031296K->147328K(1031296K), 0.2108710secs] 2185170K->1462927K(2998400K), 0.2172180 secs] [Times: user=0.76sys=0.00, real=0.21 secs]

2013-08-31T22:02:26.119+0800:183893.765: [GC 183893.766: [ParNew: 1031296K->147328K(1031296K), 0.1451320secs] 2346895K->1587229K(2998400K), 0.1455830 secs] [Times: user=0.53sys=0.00, real=0.15 secs]

2013-08-31T22:02:26.265+0800: 183893.912: [GC [1CMS-initial-mark: 1439901K(1967104K)] 1587233K(2998400K), 0.1598020 secs][Times: user=0.17 sys=0.00, real=0.16 secs]

2013-08-31T22:02:26.425+0800:183894.072: [CMS-concurrent-mark-start]

2013-08-31T22:02:29.819+0800:183897.466: [GC 183897.466: [ParNew: 1031296K->103529K(1031296K), 0.0524950secs] 2471197K->1543430K(2998400K), 0.0529010 secs] [Times: user=0.20sys=0.00, real=0.06 secs]

2013-08-31T22:02:32.323+0800:183899.969: [CMS-concurrent-mark: 5.843/5.897 secs] [Times: user=12.46sys=0.00, real=5.90 secs]

………

2013-08-31T22:18:18.671+0800:184846.318: [GC [1 CMS-initial-mark: 1777901K(1967104K)] 2803182K(2998400K),2.0194650 secs] [Times: user=2.11 sys=0.00, real=2.02 secs]

2013-08-31T22:18:20.692+0800:184848.339: [CMS-concurrent-mark-start]

2013-08-31T22:18:27.498+0800: 184855.145: [GC 184855.145:[ParNew: 1031296K->1031296K(1031296K), 0.0000250 secs]184855.145:[CMS2013-08-31T22:18:28.559+0800: 184856.206: [CMS-concurrent-mark: 7.857/7.867secs] [Times: user=8.37 sys=0.00, real=7.87 secs]

 (concurrent mode failure): 1777901K->159388K(1967104K), 3.6031330 secs]2809197K->159388K(2998400K), [CMS Perm : 128258K->128085K(262144K)],3.6035670 secs] [Times: user=3.76 sys=0.00, real=3.60 secs]

2013-08-31T22:30:03.029+0800: 185550.675: [GC 185550.676:[ParNew: 883968K->135189K(1031296K), 0.0473080 secs] 1043356K->294577K(2998400K), 0.0476930 secs] [Times: user=0.19 sys=0.00,real=0.05 secs]

3.      Scout新机房重启机器+任务适度分散后的日志:

2013-09-02T16:11:06.354+0800:2575.579: [GC 2575.580: [ParNew: 911104K->141685K(1031296K), 0.1151870 secs]1374122K->604703K(2997376K), 0.1154760 secs] [Times: user=0.45 sys=0.01,real=0.12 secs]

2013-09-02T16:11:11.069+0800:2580.294: [GC 2580.295: [ParNew: 1025653K->147328K(1031296K), 0.3611170secs] 1488671K->799003K(2997376K), 0.3614160 secs] [Times: user=1.16sys=0.12, real=0.37 secs]

2013-09-02T16:11:13.095+0800:2582.321: [GC 2582.321: [ParNew: 1031296K->147328K(1031296K), 0.2747180secs] 1682971K->988387K(2997376K), 0.2750100 secs] [Times: user=0.86 sys=0.10,real=0.28 secs]

2013-09-02T16:11:15.055+0800:2584.281: [GC 2584.281: [ParNew: 1031296K->147328K(1031296K), 0.2087500secs] 1872355K->1119075K(2997376K), 0.2090190 secs] [Times: user=0.63sys=0.07, real=0.21 secs]

2013-09-02T16:11:16.980+0800:2586.206: [GC 2586.206: [ParNew: 1031296K->147328K(1031296K), 0.3975570secs] 2003043K->1273364K(2997376K), 0.3978680 secs] [Times: user=0.74sys=0.35, real=0.39 secs]

2013-09-02T16:11:19.020+0800:2588.246: [GC 2588.246: [ParNew: 1031296K->147328K(1031296K), 0.4993670secs] 2157332K->1435431K(2997376K), 0.4996340 secs] [Times: user=0.76sys=0.58, real=0.50 secs]

2013-09-02T16:11:21.179+0800:2590.405: [GC 2590.405: [ParNew: 1031296K->147328K(1031296K), 0.4948870secs] 2319399K->1597478K(2997376K), 0.4951820 secs] [Times: user=0.71sys=0.62, real=0.50 secs]

2013-09-02T16:11:21.676+0800:2590.901: [GC [1 CMS-initial-mark: 1450150K(1966080K)] 1613957K(2997376K),0.2896470 secs] [Times: user=0.29 sys=0.00, real=0.29 secs]

2013-09-02T16:11:21.966+0800: 2591.192:[CMS-concurrent-mark-start]

…………………..

    2013-09-02T16:30:55.987+0800: 3765.213:[GC[YG occupancy: 1011014 K (1031296 K)]3765.213: [Rescan (parallel) ,1.3702730 secs]3766.583: [weak refs processing, 0.0000220 secs] [1 CMS-remark:1767514K(1967104K)] 2778529K(2998400K), 1.3771890 secs] [Times: user=5.08sys=0.01, real=1.38 secs]

2013-09-02T16:30:57.365+0800:3766.590: [CMS-concurrent-sweep-start]

2013-09-02T16:30:59.090+0800:3768.316: [CMS-concurrent-sweep: 1.718/1.725 secs] [Times: user=1.79 sys=0.01,real=1.72 secs]

2013-09-02T16:30:59.090+0800:3768.316: [CMS-concurrent-reset-start]

2013-09-02T16:30:59.095+0800:3768.321: [CMS-concurrent-reset: 0.005/0.005 secs] [Times: user=0.00 sys=0.00,real=0.01 secs]

2013-09-02T16:31:00.675+0800: 3769.901:[GC 3769.901: [ParNew: 1031296K->1031296K(1031296K), 0.0000220secs]3769.901: [CMS: 1767508K->151416K(1967104K), 2.4975950 secs]2798804K->151416K(2998400K), [CMS Perm : 120361K->119338K(262144K)],2.4979560 secs] [Times: user=2.49 sys=0.01, real=2.50 secs]

 

   对比日志可以看出:

1.      新机房与老机房在22点报表任务运行后,到达GC的时间差不多,但在到达过程中,新机房的年轻代GC不足10次,而老机房则超过300次,这是因为老机房的年轻代只有70多M,而新机房为1G,故新机房的年轻代GC比老机房少;

2.      老机房在报表任务结束后(22:06),内存迅速下降,而新机房在报表任务结束后,一直持续到18分的时候,才因为年轻代垃圾回收时报concurrent mode failure,从而导致FULL GC,最终内存才迅速下降;

但由此并不能断定老机房存在必须FULL GC才能消除的引用,因为在例3中,虽然也比任务结束时间晚了十几分钟,但最终确实是在CMS的垃圾收集中内存下降下来了,而未触发FULL GC;

,不过两次回收的特别处在于都触发了对方法区的回收,即[CMS Perm : 120361K->119338K(262144K)],2.4979560 secs],由此大可致推断垃圾回收跟方法区有关,具体可能是方法区中的常量,静态变量(这个不太可能,回收静态变量的前提是回收类,回收类的前提是加载该类的classLoader要被回收);

 

8.      确定垃圾回收期间的相关任务

   这个一般是通过查看定时任务或日志来发现当时正在执行的比较特别的任务,因为该问题之前出现过,故一看时间点就知道是催收的报表任务引发的;

   如果内存的增长回落与任务的开始结束时间完全符合,则可以确定是该任务的问题,并进行相关优化;

   本次的特别之处在于,相关报表任务结束后二十多分钟里,虽然一直在垃圾回收,但内存仍未降下来,故仍需继续查找是否有其它原因;

  

9.      查看DUMP文件

  通过DUMP文件可以确定对象的引用关系,以及比较占内存的大对象,来为程序优化以及问题定位提供更多线索;

1.      Dump文件的查看方式:

安装 Xming-6-9-0-31-setup.exe: http://ncu.dl.sourceforge.net/project/xming/Xming/6.9.0.31/Xming-6-9-0-31-setup.exe
SecureCRT 打开 forword x11 packets 选项

出现如下界面:


其实EclipseMemory Analyzer在本地eclipse上也可以运行,但因为dump文件一般比较大,通常几个G,传到本地比较费时间,且解析dump比较费内存,故通过远程连接的方式查看;

上面的几步实际上是开远程界面的操作;

 

2.      查看内存对象:


虽然内存占用有2G多,但是看到的图上所有内存竟然只有150M, 让PE帮忙DUMP了2次都是这个结果,后来PE讲是把unreached object删掉了,所以如果看到内存占用与GC日志严重不符,需检查dump命令及参数;

正确的图是这样子的:


可以看到,最大的一块Suspect的内存有1G,这就说明有占用内存较大的单个,再看该块Suspect内存的详细分析:

 

 该图的引用关系是:右下方的对象引用左上方的对象,而对象左边的黑体字代表的是被引用对象的变量名,Shallow Heap代表的是对象自身所占内存(不包含引用对象),而Retained Heap则代表对象及其引用对象所占内存;

 可以看到主要引用关系如下:XSSFSheet-->Object[]-->ArrayList(elementData)-->XSSFWorkbook(sheets)-->HashMap$Entry(value)-->HashMap$Entry(next)-->???

    即HashMap的内部类Entry的next变量引用了另一个Entry对象,而该对象的value变量值即为XSSFWorkbook对象,查看XSSFWorkbook类代码,可发现定义了一个List的sheets对象,该对象的初始化方法用的是ArrayList……………                                                                

 从该图可以看到,这块内存其实是可以被回收掉的,因为其根引用对象都非当前线程对象(至于该图是否能显示来自方法区的对象引用,我不太确定,有知道的同学麻烦帮忙补充一下)

 因为不知是否有来自方法区的引用,故对Unreachable的对象又进行了跟进,即,查看程序中获取以及操作XSSFWorkbook对象的所有代码,看是否有将其存入软引用,放入类似Factory类的静态变量等操作,均未发现;

 

10.  对比环境信息

      新老机房操作系统:都为64位,新老机房JVM:都为64位,新老机房JDK版本:老机房:1.6.25 新机房: 1.6.26

         新老机房启动参数:内存参数:

老机房年轻代70多M,新机房1G多

                                老机房年老代2.1G,新机房年老代1.9G

老机房堆内存2.2G,新机房堆内存接近3G    

启动参数:

                            新机房多-XX:+CMSFullGCsBeforeCompaction=5 执行5次FULL GC后对内存压缩  默认是0

         因为其它地方均未发现异常,故只能由此来推测,CMS收集器会导致内存碎片的问题,而CMSFullGCsBeforeCompaction=5则使得必须5次FULL GC后才会进行内存整理,故会导致内存碎片,也就是说任务结束后的内存持续占用较大可能是内存碎片导致的(当然也有另外的可能,就是巨大对象(如本例中的1G对象)的内存碎片会导致CMS收集器无法完整识别该对象的引用,特别是存在循环引用的情况下),当因为concurrent mode failure退化为serial old收集时,因为内存整理故而内存占用迅速下降;

三、解决方法及总结       

11.  最终措施:

      找PE更改启动参数:-Xmn即年轻代大小由原来的内存的3/8改为1/8,去除-XX:+CMSFullGCsBeforeCompaction参数;

      经过昨天晚上和今天下午两次报表任务的内存观察,通过在几次的年老代回收后内存即可下降,不再有频繁的年老代回收和内存占用超过90%的警告;

 

12.  原因分析:

      实际上新机房的参数应该是标配,即年轻代占内存的3/8是sun的推荐配置,-XX:+CMSFullGCsBeforeCompaction参数可有效减少系统停顿时间,因为内存整理是要暂停应用程序的;

      年轻代一般应该较大的原因是:对于大部分的web服务器来说,处理大量的请求是其主要的内存占用来源,而大量的请求会在很短时间内处理完,也就代表为此new的对象的生命周期是很短的,这种情况下年轻代较大可有效减少年老代的对象数量,从而减少因年老代的垃圾收集而造成的系统停顿;因为年老代收集的初始标记、重新标记阶段(可参阅下面的CMS收集器介绍)都是需要暂停程序的;

      而在scout系统中,该案例中的内存占用的主要来源是后台的定时任务,而非前台请求,且该任务占用内存大且持续时间长(相对于一般web请求的持续时间表而言),故会导致进行年老代的对象大量增加,此时年轻代比较大并不能有效减少进行年老代的对象数量,反而因为年轻代大而导致年老代内存小(年轻代+年老代的内存问题是一定的,由-Xms,–Xmx参数指定),从而导致年老代垃圾回收的触发点降低(本例中是年老代内存占用超过年老代内存问题的70%),从而引发更频繁的年老代GC;

     

      内存整理会导致系统停顿,在大部分的web系统中是应该尽量避免的,但scout系统的用户数不超过10个,故而偶尔的零点几秒停顿甚至一至两秒的停顿,完全是不可能被用户察觉到的

13.  总结:一般情况下,内存问题处理的步骤通常是通过GC日志的内存变化点定位到相应的任务,再通过DUMP文件的辅助定位到任务更精确的代码层,从而实现解决问题的目的;

      本例中最终的做法是调参数,但因为JVM比较复杂,故参数的调整必须要慎重,本例中在调这些参数前,其实内心也是没有十足把握的,只是因为代码层面实在解决不了,才来尝试了一下;

 

    

 

介绍:

CMS收集器

CMS(ConcurrentMark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合这类应用的需求。

从名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于“标记-清除”算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为4个步骤,包括:

初始标记(CMS initial mark)

并发标记(CMS concurrentmark)

重新标记(CMS remark)

并发清除(CMS concurrentsweep)

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

由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的。通过图3-10可以比较清楚地看到CMS收集器的运作步骤中并发和需要停顿的时间。


 
 

CMS是一款优秀的收集器,它的最主要优点在名字上已经体现出来了:并发收集、低停顿,Sun的一些官方文档里面也称之为并发低停顿收集器(Concurrent Low Pause Collector)。但是CMS还远达不到完美的程度,它有以下三个显著的缺点:

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

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

强引用,软引用,弱引用,虚引用相关介绍

http://www.cnblogs.com/blogoflee/archive/2012/03/22/2411124.html

 

你可能感兴趣的:(内存频繁GC问题查找分享)