概述 1WAS环境OOM
基于WebSphere Application Server(Was) 构建的企业应用,时常会出现性能问题,在严重的情况下还会发生内存溢出,这是一件很让人恼怒的事情。在Was运行的时候,内存溢出,会生成大量的溢出文件,如Javacore, Heapdump等文件,占用了大量的磁盘空间。在这种情况下,时常会出现一连串的系统问题,如部署在Was的所有应用服务都报错,Was连控制台也无法访问等。
为解决问题,我们通常会选择重新启动整个Was服务器,然后分析运行日志SystemOut.log、SystemErr.log、native_stdout.log、native_stderr.log 和系统内存溢出的时候产生的Javacore、Heapdump文件来寻找出问题。
2堆内存
应用服务器在运行过程中需要创建很多对象,而在应用服务器的堆空间大小有限的情况下,请求进程不断申请空间来创建与存放对象,在达到上限时而服务器又没能释放出空间来处理申请空间的请求就会出现内存溢出情况。这就像吹气球,当气球中的气体到达一定程度时,气球就会被撑爆。通常在投入生产的系统中,出现溢出一般都是对象分配不合理导致的。
3对象与对象管理
在Java的体系中,所有的类作为一个对象(包括Jdk本身和记平台提供的类,应用中由开发人员编写的类),都是直接或者间接继承了Java.lang.object产生的。这些类被创建的时候都会向Jvm堆申请一定的内存空间存放,因此在Jvm堆空间里会存放各式各样的对象,有的是静态类型,有的是私有类型等等,而这些对象都是通过垃圾收集器进行管理的。
垃圾收集器(Garbage Collection,我们通常称之为GC)。Java语言通过垃圾收集器管理内存中的对象。但如果对象分配与使用不合理,也会导致垃圾回收在性能上拖Jvm的后腿。为了解决性能问题,GC则是一个让我们不能忽视的问题。根据不同的应用场景,选择适合的GC策略,可以帮助系统性能在一定程度上得到显著的优化。
垃圾回收策略
垃圾收集器为了快速清除在JVM堆中不再使用的对象,从而释放出空间来供其他请求创建对象,常见的方法有引用计数方法和对象引用遍历方法等算法,目前主流的方法为对象引用遍历方法。通过这些高效的算法实现的GC策略有许多种,他们都是针对各种应用场景而产生的,我们可以通过在JVM启动参数中设置,来调整GC策略。
1策略种类及功能说明
适用IBM SDK5.0以上版本
序号 | 策略 | 选项 | 描述 | 备注 |
1 | 针对吞吐量进行优化 | -Xgcpolicy:optthruput | WAS默认策略。对于吞吐量比短暂的 GC 停顿更重要的应用程序,通常使用这种策略。每当进行垃圾收集时,应用程序都会停顿。 | |
2 | 针对停顿时间进行优化 | -Xgcpolicy:optavgpause | 通过并发地执行一部分垃圾收集,在高吞吐量和短 GC 停顿之间进行折中。应用程序停顿的时间更短。 | |
3 | 分代并发 | -Xgcpolicy:gencon | 以不同方式处理短期存活的对象和长期存活的对象。采用这种策略时,具有许多短期存活对象的应用程序会表现出更短的停顿时间,同时仍然产生很好的吞吐量。 | 推荐使用(从WAS8开始默认采用) |
4 | 子池 | -Xgcpolicy:subpool | 采用与默认策略相似的算法,但是采用一种比较适合多处理器计算机的分配策略。建议对于有 16 个或更多处理器的 SMP 计算机使用这种策略。这种策略只能在 IBM System i? System p? 和 System z?平台上使用。需要扩展到大型计算机上的应用程序可以从这种策略中受益。 |
2选择合适的GC策略
一般是通过吞吐量和停顿时间两个参数来衡量GC效率是否适合当前应用系统。
? 吞吐量
是应用程序处理的数据量。衡量吞吐量的标准是与具体应用程序相关的。
? 停顿时间
是垃圾收集器将所有应用程序线程停下来,从而对堆进行收集所经历的时间。
为了选择适合的GC策略,可以在业务应用系统打开详细垃圾回收,收集系统在运行过程中的GC日志,通过分析GC日志,来判断当前GC策略是否符合当前应用系统。合理的创建使用对象,选择适合的GC策略,往往能让性能有一定的提高,也减少了内存溢出的可能性,或者缩短系统停顿的时间。
WAS分代策略 1GC日志分析
以下是一段发生在IBM SDK5.0版本上配置了分代并发策略所产生的GC日志。
<!—距离上次GC间隔时间为6.787 秒 <af> 表示本次垃圾回收是因为分配失败而引发的,如果标签开头写着sys,则表示应用中有显示调用GC,如System.gc()。一般情况下不建议显示调用GC,当然也可以通过配置防止显示GC, 在JVM启动参数中加入-Xdisableexplicitgc参数屏蔽显式GC。 Type=“nursery” :这次GC的类型是新生代方式,因为新生代的分配失败而进行垃圾回收了。 Id=”3365”代表GC发生在新生代,已经重复执行了3365次 timestamp:GC的执行时间 --> <af type="nursery" id="3365" timestamp="三 1月 06 16:09:03 2010" intervalms="6787.454"> <!—这里记录需要申请的堆大小为710824b 约694KB --> <minimum requested_bytes="710824" /> <!—GC准备开始前 使用时间为 0.224秒 --> <time exclusiveaccessms="0.224" /> <!—GC前堆空间使用情况,可以看到剩余567408B,不能满足空间的申请要求了,申请空间是710824B,因此就需要进行GC --> <nursery freebytes="567408" totalbytes="42694656" percent="1" /> <!-- GC前的堆空间情况,JVM堆大小为1.2左右了,空闲的空间为650mb左右,大致空闲空间为48% Tenured:长存(tenured) 区域 Soa:小对象使用区域;就是 “正常的” 堆。所有对象最初都分配给小对象区域,但是如果小区域已经分配完了,则将大于 64KB 的对象分配给大对象区域。 Loa:大对象使用区域;大对象区域是堆中保留给大对象分配的一小片区域。如果应用程序不需要大对象区域(也就是应用程序不分配任何大对象),内存管理例程会快速将大对象区域缩减为空,这样,整个堆都可以供 “正常的” 分配使用。 --> <tenured freebytes="666933424" totalbytes="1365881856" percent="48" > <soa freebytes="654641328" totalbytes="1353589760" percent="48" /> <loa freebytes="12292096" totalbytes="12292096" percent="100" /> </tenured> <!—GC后堆空间使用情况 --> <!—详细gc情况 type="scavenger":垃圾回收类型为清理过程。 id="3365": 意为执行力3365次清理,并且没有发生过全局GC,如 <gc type=”global”>。 <flipped objectcount="15558" bytes="3973128" />:说明将要把15558个存活下来的对象被复制到了幸存区(survivor space) <tenured objectcount="14791" bytes="14233400" />:说明将要把14791个对象则经过多次幸存区的复制,被转移到了长存区(tenured)
<scavenger tiltratio="65" />:清理的倾斜比率为65% <refs_cleared soft="73" weak="61" phantom="0" />:清理了存在引用对象的数目. 弱引用为61、软引用为73,和虚引用为0. 弱引用、软引用和虚引用允许灵活的缓存,能够改进应用程序的内存特性。如果引用对象过多大于1000,GC停顿时间就会受到影响。因此我们需要检查是否存在过多的应用对象。 <finalization objectsqueued="40" />:收尾器,存在需要在GC后,要调用的自定义方法的对象。
--> <gc type="scavenger" id="3365" totalid="3709" intervalms="6788.531"> <flipped objectcount="15558" bytes="3973128" /> <tenured objectcount="14791" bytes="14233400" /> <refs_cleared soft="73" weak="61" phantom="0" /> <finalization objectsqueued="40" /> <scavenger tiltratio="65" />
<!—GC后的情况 nursery区 1、清理了出空闲堆大小为39108112b 约为38191.515625KB,约为37MB 2、 总大小为44144640B,约43110KB 约为42MB --> <nursery freebytes="39108112" totalbytes="44144640" percent="88" tenureage="1" />
<!— Tenured长存区总大小为1365881856B,约为1.27GB 空闲堆大小为 651143448B 620MB -->
<tenured freebytes="651143448" totalbytes="1365881856" percent="47" > <soa freebytes="638851352" totalbytes="1353589760" percent="47" /> <loa freebytes="12292096" totalbytes="12292096" percent="100" /> </tenured> <time totalms="25.029" /> </gc>
<!--这里再次打印需要申请的堆大小为710824b 约694KB --> <minimum requested_bytes="710824" />
<!--通过比较,就可以知道当前次的GC情况,及堆空间的使用率 <time totalms="26.386" /> 总的GC时间为26.386ms --> <nursery freebytes="38397288" totalbytes="44144640" percent="86" /> <tenured freebytes="651143448" totalbytes="1365881856" percent="47" > <soa freebytes="638851352" totalbytes="1353589760" percent="47" /> <loa freebytes="12292096" totalbytes="12292096" percent="100" /> </tenured> <time totalms="26.386" /> </af> <!-- 经过垃圾回收,新生代的空间剩余86%,因为分配失败发生在新生代,进行的是快速的Minor gc,所以长存区的可用空间依然是47%,这次回收的时间为26.386ms。而 在长存区发生的是Major gc,回收时间一般要长于Minor gc,一般健康的JVM 环境Minor gc:Minor gc=1:10。 --> |
打开详细垃圾回收,我们就可以获得详细垃圾回收情况,根据以上的GC日志的分析,我们则可以了解到系统GC和记平台提供了什么样的信息给我们。通过这些信息,我们可以分析得出当前GC策略是否需要调整。(当然这个与您实际生产得出的GC日志分析结果有关)。
2调整GC策略判断标准
在分析的过程里,我们需要着重查看以下几点:
? GC频率:
多长时间执行一次GC,如果GC频繁的话,则需要检查系统现在的运行情况是否合理
? GC类型+GC申请空间大小+新生代空间大小:
分析GC是否合理
? 各分代区域的大小:
检查当前JVM堆的使用是否合理,新生代的空间大小是否符合当前业务场景要求。长存区的空间大小是否合理,再联合JVM配置的堆配置大小分析,堆的使用是否存在问题,因为虽然进行了GC,但堆空间的使用如不能满足要求的话,易导致系统出现全局GC,在压力过大的情况下还会内存溢出。
? 重点检查GC过程中长存区的变化:
是否存在大对象,如果有的话,可以考虑-Xlp参数优化
? GC过程中是否清理了过多引用对象:
如有则需要检查应用
? GC整个过程消耗的时间是否过长:
过长的GC会直接影响到系统的性能,当然这个也与GC类型有关,全局GC,清理长存区的GC时间相对会长点,但是这些类型的GC不会很频繁。
通过详细分析GC日志,从而协助我们进一步了解JVM堆的使用情况,最后得出当前GC策略是否符合业务应用使用场景,而更换其他GC策略则需要配合当前业务场景与JVM堆使用情况去选择适合的GC策略。
3IBM Java虚拟机其他选项
适合IBM JDK 设置参数简单介绍:
序号 | 选项 | 描述 |
11 | -Xlp | 此设置与IBM JVM配合使用,以使用大页16MB来分配堆 |
22 | Xlp64k | 此参数与IBM JVM配合使用,以使用64K字节页大小来分配堆 |
33 | -Xnoclassgc | 关闭类垃圾回收,可以消除由于多次装入和卸载同一个类而造成地开销 |
44 | -Xnocompactgc | 该参数完全关闭压缩。虽然在性能方面有短期的好处,最终应用程序堆将变得支离破碎,即使堆中有足够的自由空间也会导致 OutOfMemory 错误 |
55 | -Xcompactgc | 使用该参数将导致每个垃圾收集周期都执行压缩,无论是否有必要。JVM 在压缩时要做大量的决策,在普通模式下会推迟压缩 |
66 | -Xgcthreads | 该参数控制 JVM 在启动过程中创建的垃圾收集帮助器线程个数。对于 N-处理器机器,默认的线程数为 N-1。这些线程和记平台提供并行标记和并行清理模式中的并行机制 |
| 北京 | 上海 | 广州 | 成都 |
4008-906-960