欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

用Java获取full GC的次数?(2)

程序员文章站 2022-05-06 20:57:40
...
接着[url=http://rednaxelafx.iteye.com/blog/790015]上一帖[/url],再谈谈一个Java进程要获取自己的full GC次数的方法,同样是高度平台相关的办法。

大家如果熟悉JDK 6的内建工具,或许已经知道可以通过[url=http://download.oracle.com/javase/6/docs/technotes/tools/share/jstat.html]jstat[/url]工具很轻松的从外部得知一个Java进程的GC统计信息,其中就包括了full GC的次数。
假定我们相信jstat的数据是准确的,那么只要跟它从同一来源获取数据就可以保证我们拿到正确的full GC次数信息了。
查看[url=http://openjdk.java.net/]OpenJDK[/url] 6中jstat的一个源文件,[url=http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/9216ec4e4c14/src/share/classes/sun/tools/jstat/resources/]jdk/src/share/classes/sun/tools/jstat/resources/[/url][url=http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/9216ec4e4c14/src/share/classes/sun/tools/jstat/resources/jstat_options]jstat_options[/url],可以看到jstat -gcutil输出的YGC(young GC)与FGC(full GC)值分别是从下面两个定义而来的:
column {
header "^YGC^" /* Young Generation Collections */
data sun.gc.collector.0.invocations
align right
width 6
format "0"
}
column {
header "^FGC^" /* Full Collections */
data sun.gc.collector.1.invocations
align right
width 5
scale raw
format "0"
}

也就是说,在Oracle (Sun) HotSpot上,通过[url=http://java.sun.com/performance/jvmstat/]jvmstat API[/url],找到名字为 [color=brown]"sun.gc.collector.0.invocations"[/color] 与 [color=brown]"sun.gc.collector.1.invocations"[/color] 的[url=http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/9216ec4e4c14/src/share/classes/sun/jvmstat/monitor/Monitor.java]Monitor[/url]对象,我们就可以拿到YGC与FGC对应的值了。别的JVM即便支持该API,Monitor的名字也可能会不同;在Oracle (BEA) JRockit R28上,两者对应的名字分别为 [color=brown]"jrockit.gc.latest.yc.number"[/color] 与 [color=brown]"jrockit.gc.latest.oc.number"[/color] 。

在底层,HotSpot通过[url=http://hg.openjdk.java.net/jdk6/jdk6/hotspot/file/tip/src/share/vm/runtime/perfData.hpp]perfData[/url]接口来提供这些数据;注册数据的实现在[url=http://hg.openjdk.java.net/jdk6/jdk6/hotspot/file/tip/src/share/vm/services/]services[/url]目录里。

OpenJDK官网上也有一个相关文档:[url=http://openjdk.java.net/groups/hotspot/docs/Serviceability.html#bjvmstat]HotSpot Jvmstat Performance Counters[/url]

提一下jvmstat文档上说的一个注意点:
[quote]The instrumented HotSpot JVM exports a set of instrumentation objects, or counters as they are typically called. The set of counters exported by a JVM is not static, as a JVM may create certain counters only when appropriate arguments are specified on the command line. Furthermore, different versions of a JVM may export very different sets of instrumentation. [color=red]The names of these counters and the data structures used to represent them are considered private, uncommitted interfaces to the HotSpot JVM. Users should not become dependent on any counter names, particularly those that start with prefixes other than "[b]java.[/b]".[/color][/quote]
也就是说我们最好别依赖这些计数器的名字。不过反正这帖只是介绍些hack的办法而已,不管了 :lol:

下面用Groovy代码来演示一下。
使用jvmstat API需要指定vmid,对多数系统上本地Java进程的VMID就是PID。这里正好用上[url=http://rednaxelafx.iteye.com/blog/716918]以前一帖[/url]介绍的获取进程自己的PID的方式。

import java.lang.management.ManagementFactory
import sun.jvmstat.monitor.*;

class Runtimes {
static int getOwnPid() {
def name = ManagementFactory.runtimeMXBean.name
name[0..<name.indexOf('@')] as int
}
}

class GCStats {
// Oracle (Sun) HotSpot
static final String YOUNG_GC_MONITOR_NAME = 'sun.gc.collector.0.invocations'
static final String FULL_GC_MONITOR_NAME = 'sun.gc.collector.1.invocations'

// Oracle (BEA) JRockit
// static final String YOUNG_GC_MONITOR_NAME = 'jrockit.gc.latest.yc.number'
// static final String FULL_GC_MONITOR_NAME = 'jrockit.gc.latest.oc.number'

static final Monitor youngGCMonitor;
static final Monitor fullGCMonitor;

static {
def vmId = new VmIdentifier(Runtimes.ownPid as String)
def interval = 0
def monitoredHost = MonitoredHost.getMonitoredHost(vmId)
def monitoredVm = monitoredHost.getMonitoredVm(vmId, interval)
youngGCMonitor = monitoredVm.findByName(YOUNG_GC_MONITOR_NAME)
fullGCMonitor = monitoredVm.findByName(FULL_GC_MONITOR_NAME)
}

static int getYoungGCCount() {
youngGCMonitor.value
}

static int getFullGCCount() {
fullGCMonitor.value
}
}

可以看到,要获取young GC与full GC次数的读数很简单,找到合适的Monitor对象后,每次读一下value属性就好了。
在Groovy shell里演示一下使用情况,在Sun JDK 6 update 20/Windows XP SP3上跑:
D:\>\sdk\groovy-1.7.2\bin\groovysh
Groovy Shell (1.7.2, JVM: 1.6.0_20)
Type 'help' or '\h' for help.
--------------------------------------------------
groovy:000> GCStats.fullGCCount
===> 0
groovy:000> System.gc()
===> null
groovy:000> GCStats.fullGCCount
===> 1
groovy:000> System.gc(); System.gc(); System.gc()
===> null
groovy:000> GCStats.fullGCCount
===> 4
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> System.gc(); System.gc(); System.gc()
===> null
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> GCStats.fullGCCount
===> 7
groovy:000> quit


这次也顺便演示一下在Oracle JRockit R28/Windows XP SP3上跑:
D:\>\sdk\groovy-1.7.2\bin\groovysh
Groovy Shell (1.7.2, JVM: 1.6.0_17)
Type 'help' or '\h' for help.
--------------------------------------------------
groovy:000> GCStats
===> class GCStats
groovy:000> GCStats.youngGCCount
===> 1
groovy:000> System.gc()
===> null
groovy:000> GCStats.youngGCCount
===> 2
groovy:000> System.gc(); System.gc(); System.gc()
===> null
groovy:000> GCStats.youngGCCount
===> 5
groovy:000> GCStats.fullGCCount
===> 0
groovy:000> quit

可以看到System.gc()引发的是young GC而不是full GC吧? ^_^