对tomcat 7.0 下堆内存告警的情况进行了总结,线上jdk1.6使用的GC策略为:-XX:+UseParallelGC ,即新生代为并行GC年老代为串行GC,jdk1.7默认GC方式为-XX:+UseParallelOldGC ,即新生代和年老代均为并行GC。修改方式为,修改jdk1.7的GC策略为-XX:+UseConcMarkSweepGC (并发GC策略,仅标记阶段会暂停工作线程,响应优先,会较多较快的执行FullGc)。
监控报表的应用等也都遇到了堆内存超过80%的问题,其中一些应用是jdk1.6,也有jdk1.7。参照调整
原选项(或默认选项)
-XX:+UseParallelGC
修改为:
-XX:+UseConcMarkSweepGC
后,观察发现,young gc的次数明显增多,Full gc没有明显增长,堆内存随着时间推移仍然缓慢增长,待增长到一定时间,仍然有堆内存超过80%的告警。告警之后,Full gc之后,告警消失。于是分析,应该是年轻代内存上限不够,另外,能否通过适当增加Full gc的次数,对年老代进行及时回收。通过参考相关文档,旧生代并发回收的触发时机为:
1、当旧生代空间使用到一定比率时触发;
JDK V 1.6中默认为92%,可通过PrintCMSInitiationStatistics(此参数在V 1.5中不能用)来查看这个值到底是多少;
可通过CMSInitiatingOccupancyFraction来强制指定,默认值并不是赋值在了这个值上,是根据如下公式计算出来的:
((100 – MinHeapFreeRatio) +(double)(CMSTriggerRatio * MinHeapFreeRatio) / 100.0)/ 100.0;
MinHeapFreeRatio默认值: 40 CMSTriggerRatio默认值: 80
2、当perm gen采用CMS收集且空间使用到一定比率时触发;
perm gen采用CMS收集需设置:-XX:+CMSClassUnloadingEnabled
JDK V 1.6中默认为92%;
可通过CMSInitiatingPermOccupancyFraction来强制指定,同样,它是根据如下公式计算出来的:
((100 – MinHeapFreeRatio) +(double)(CMSTriggerPermRatio* MinHeapFreeRatio) / 100.0)/ 100.0;
MinHeapFreeRatio默认值: 40 CMSTriggerPermRatio默认值: 80
3、Hotspot根据成本计算决定是否需要执行CMS GC;
可通过-XX:+UseCMSInitiatingOccupancyOnly来去掉这个动态执行的策略。
4、外部调用了System.gc,且设置了ExplicitGCInvokesConcurrent;
需要注意,在JDK 6中,在这种情况下如应用同时使用了NIO,可能会出现bug。
另外有两个重要的表格参考:
新生代GC方式 | 旧生代和持久代GC方式 | |
-XX:+UseSerialGC | 串行GC | 串行GC |
-XX:+UseParallelGC | PS GC | 并行MSC GC |
-XX:+UseConcMarkSweepGC | ParNew GC | 并发GC
当出现concurrent Mode failure时采用串行GC |
-XX:+UseParNewGC | 并行GC | 串行GC |
-XX:+UseParallelOldGC | PS GC | 并行Compacting GC |
-XX:+UseConcMarkSweepGC
-XX:-UseParNewGC |
串行GC | 并发GC
当出现Concurrent Mode failure或promotion failed 时则采用串行GC |
不支持的组合方式 | 1、-XX:+UseParNewGC –XX:+UseParallelOldGC
2、-XX:+UseParNewGC –XX:+UseSerialGC |
GC方式 | 常用参数(-Xms –Xmx –Xmn –XX:PermSize –XX:MaxPermSize) | |
新生代可用GC | 串行GC | -XX:SurvivorRatio,默认为8,代表eden:survivor;
-XX:MaxTenuringThreshold,默认为15,代表对象在新生代经历多少次minor gc后 才晋升到旧生代; |
PS GC | -XX:InitialSurvivorRatio,默认为8,代表new gen:survivor;
-XX:SurvivorRatio,默认值对于PS GC无效,但仍然可设置,代表eden:survivor; -XX:-UseAdaptiveSizePolicy,不允许PS GC动态调整eden、s0、s1的大小, 此时-XX:MaxTenuringThreshold也可使用; -XX:ParallelGCThreads,设置并行GC的线程数。 |
|
ParNew GC | 同串行。 | |
旧生代和持久代可用GC | 串行GC | 无特殊参数。 |
并行GC
(包括MSC、 Compacting) |
-XX:ParallelGCThreads,设置并行GC的线程数。
-XX:+ScavengeBeforeFullGC,Full GC前触发Minor GC |
|
并发GC | -XX:ParallelCMSThreads,设置并发CMS GC时的线程数;
-XX:CMSInitiatingOccupancyFraction,当旧生代使用比率占到多少百分比时触发CMS GC; -XX:+UseCMSInitiatingOccupancyOnly,默认为false,代表允许hotspot根据成本来决定 什么时候执行CMS GC; -XX:+UseCMSCompactAtFullCollection,当Full GC时执行压缩; -XX:CMSMaxAbortablePrecleanTime=5000,设置preclean步骤的超时时间,单位为毫秒; -XX:+CMSClassUnloadingEnabled,Perm Gen采用CMS GC回收。 |
经过以上分析,调整内存参数为:
-Xms4096m -Xmx4096m -XX:MaxPermSize=512m -XX:+UseConcMarkSweepGC -Xmn2048M -XX:+UseCMSInitiatingOccupancyOnly -XX:+CMSClassUnloadingEnabled
具体含义如下:
Xms和Xmx就不用说了,
-XX:MaxPermSize:最大持久代内存512M
-XX:+UseConcMarkSweepGC: 使用年轻代和年老代都并行gc的方式,并且gc的时间短,old gc的次数取决于stop the world(中断响应)的次数,尽量减少对应用响应的影响。
-Xmn2048M:设置新生代内存的上限大小为2g,这是为了减少young gc的次数,因为默认内存分配是会从新生代内存中分配的。
-XX:+UseCMSInitiatingOccupancyOnly: 不加这个选项的话,根据成本计算决定是否需要执行CMS gc,加上这个选项,当旧生代空间使用率达到92%时会无条件执行Full GC。
-XX:+CMSClassUnloadingEnabled :CMS收集器默认不会对永久代进行垃圾回收。如果希望对永久代进行垃圾回收,需要设置此选项。
其实还有两个选项可以考虑:
CMSInitiatingOccupancyFraction: 当旧生代空间使用到一定比率时, 强制执行旧生代的Full GC。默认为92%。这个参数如果设置为50左右的话,Full GC的次数会非常频繁。可能会对应用的性能造成一定影响。
CMSInitiatingPermOccupancyFraction: 当持久代里的使用比例达到一定比率时,进行持久代内存的回收,默认为92%。如果调低的话,也会明显增多持久代gc的次数。
但这个选项也可能对应用的性能造成影响,所以使用需要谨慎。
调整前的堆内存一天内变化趋势,可以看到虽然在调整,但是整体堆内存使用仍然呈增长态势,可以分析是Full GC的次数不够。