GC数据分析工具PMAT
在WebSphere应用服务器的日志目录下,native_stderr.log文件就是我们需要的内存回收分析文件。我们推荐使用IBM Pattern Modeling and Analysis Tool for Java Garbage Collector 工具,简称PMAT。 PMAT工具解析JAVA SDK的详细内存回收(GC)日志,并提供统计信息,图表,分析并推荐Java堆配置。PMAT提供了丰富的图形界面来显示Java堆的使用状况,从而更轻松地判断是否有内存问题发生。该工具可以从IBM的alphaWorks网站下载,只有英文版。
背景知识——WAS如何GC(以下论的是IBM的JDK)
垃圾收集可简单定义为 Java Virtual Machine (JVM) 释放不再被进程引用或使用的对象的 heap 的行为,heap 是指内存中的一个预先定义的部分,可用来管理 Java 应用程序内的资源。
这个过程有三个主要阶段:mark、sweep 和 compact:
在 mark 阶段,heap 内的所有对象均以一个 bit “标记”。然后检查这些对象看它们是否仍被引用,如果如此,这个 bit 即被删除。
在 sweep 阶段,JVM 遍历整个 heap 并删除仍具有标记 bit 的所有对象;这些对象不再被引用或使用。
compact 阶段只在一个完整的 GC 中才会运行,在这个阶段,GC 会试图将 heap 内的所有对象重新分配到 heap 内的一个更为紧缩、更为连续的较小空间。
监视 heap 使用情况的最好的方法是分析这个 verbose GC 的输出。
首先要确保该 verbose GC 在服务器上已被启用:
从 IBM WebSphere Application Server 管理控制台,导航到 Application Servers - WebSphere_Portal - Java and Process Management - Process Definition - Java Virtual Machine。
确保选中 Verbose garbage collection 旁的复选框,然后重启此服务器。
<!—距离上次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" />
<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:Major gc=1:10。 -->
打开详细垃圾回收,我们就可以获得详细垃圾回收情况,根据以上的GC日志的分析,我们则可以了解到系统GC提供了什么样的信息给我们。通过这些信息,我们可以分析得出当前GC策略是否需要调整。(当然这个与您实际生产得出的GC日志分析结果有关)。
在分析的过程里,我们需要关注几个点,来分析GC策略是否需要调整:
l GC频率:多长时间执行一次GC,如果GC频繁的话,则需要检查系统现在的运行情况是否合理
l GC类型+GC申请空间大小+新生代空间大小:分析GC是否合理
l 各分代区域的大小:检查当前JVM堆的使用是否合理,新生代的空间大小是否符合当前业务场景要求。长存区的空间大小是否合理,再联合JVM配置的堆配置大小分析,堆的使用是否存在问题,因为虽然进行了GC,但堆空间的使用如不能满足要求的话,易导致系统出现全局GC,在压力过大的情况下还会内存溢出。
l 重点检查GC过程中长存区的变化:是否存在大对象,如果有的话,可以考虑-Xlp参数优化
l GC过程中是否清理了过多引用对象:如有则需要检查应用
l GC整个过程消耗的时间是否过长:过长的GC会直接影响到系统的性能,当然这个也GC类型有关,全局GC,清理长存区的GC时间相对会长点,但是这些类型的GC不会很频繁。
以上主要是通过详细分析GC日志,从而协助我们进一步了解到JVM堆的使用情况,最后得出当前GC策略是否符合业务应用使用场景,而更换其他GC策略则需要配合当前业务场景与JVM堆使用情况去选择适合的GC策略。