天天看点

用Java获取full GC的次数

大家如果熟悉JDK 6的内建工具,或许已经知道可以通过 jstat 工具很轻松的从外部得知一个Java进程的GC统计信息,其中就包括了full GC的次数。 

假定我们相信jstat的数据是准确的,那么只要跟它从同一来源获取数据就可以保证我们拿到正确的full GC次数信息了。 

查看 OpenJDK  6中jstat的一个源文件, jdk/src/share/classes/sun/tools/jstat/resources/ jstat_options ,可以看到jstat -gcutil输出的YGC(young GC)与FGC(full GC)值分别是从下面两个定义而来的: 

Text代码  

用Java获取full GC的次数
  1. column {  
  2.   header "^YGC^"      
  3.   data sun.gc.collector.0.invocations  
  4.   align right  
  5.   width 6  
  6.   format "0"  
  7. }  
  8. column {  
  9.   header "^FGC^"      
  10.   data sun.gc.collector.1.invocations  
  11.   align right  
  12.   width 5  
  13.   scale raw  
  14.   format "0"  
  15. }  

也就是说,在Oracle (Sun) HotSpot上,通过 jvmstat API ,找到名字为  "sun.gc.collector.0.invocations"  与 "sun.gc.collector.1.invocations"  的 Monitor 对象,我们就可以拿到YGC与FGC对应的值了。别的JVM即便支持该API,Monitor的名字也可能会不同;在Oracle (BEA) JRockit R28上,两者对应的名字分别为 "jrockit.gc.latest.yc.number"  与  "jrockit.gc.latest.oc.number"  。 

在底层,HotSpot通过 perfData 接口来提供这些数据;注册数据的实现在 services 目录里。 

OpenJDK官网上也有一个相关文档: HotSpot Jvmstat Performance Counters  

提一下jvmstat文档上说的一个注意点: 

引用 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.  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 "java.".

也就是说我们最好别依赖这些计数器的名字。不过反正这帖只是介绍些hack的办法而已,不管了 

用Java获取full GC的次数

下面用Groovy代码来演示一下。 

使用jvmstat API需要指定vmid,对多数系统上本地Java进程的VMID就是PID。这里正好用上 以前一帖 介绍的获取进程自己的PID的方式。 

Groovy代码  

用Java获取full GC的次数
  1. import java.lang.management.ManagementFactory  
  2. import sun.jvmstat.monitor.*;  
  3. class Runtimes {  
  4.   static int getOwnPid() {  
  5.     def name = ManagementFactory.runtimeMXBean.name  
  6.     name[0..<name.indexOf('@')] as int  
  7.   }  
  8. }  
  9. class GCStats {  
  10.   // Oracle (Sun) HotSpot  
  11.   static final String YOUNG_GC_MONITOR_NAME = 'sun.gc.collector.0.invocations'  
  12.   static final String FULL_GC_MONITOR_NAME  = 'sun.gc.collector.1.invocations'  
  13.   // Oracle (BEA) JRockit  
  14.   // static final String YOUNG_GC_MONITOR_NAME = 'jrockit.gc.latest.yc.number'  
  15.   // static final String FULL_GC_MONITOR_NAME  = 'jrockit.gc.latest.oc.number'  
  16.   static final Monitor youngGCMonitor;  
  17.   static final Monitor fullGCMonitor;  
  18.   static {  
  19.     def vmId     = new VmIdentifier(Runtimes.ownPid as String)  
  20.     def interval = 0  
  21.     def monitoredHost = MonitoredHost.getMonitoredHost(vmId)  
  22.     def monitoredVm = monitoredHost.getMonitoredVm(vmId, interval)  
  23.     youngGCMonitor = monitoredVm.findByName(YOUNG_GC_MONITOR_NAME)  
  24.     fullGCMonitor = monitoredVm.findByName(FULL_GC_MONITOR_NAME)  
  25.   }  
  26.   static int getYoungGCCount() {  
  27.     youngGCMonitor.value  
  28.   }  
  29.   static int getFullGCCount() {  
  30.     fullGCMonitor.value  
  31.   }  
  32. }  

可以看到,要获取young GC与full GC次数的读数很简单,找到合适的Monitor对象后,每次读一下value属性就好了。 

在Groovy shell里演示一下使用情况,在Sun JDK 6 update 20/Windows XP SP3上跑: 

Groovysh代码  

用Java获取full GC的次数
  1. D:\>\sdk\groovy-1.7.2\bin\groovysh  
  2. Groovy Shell (1.7.2, JVM: 1.6.0_20)  
  3. Type 'help' or '\h' for help.  
  4. --------------------------------------------------  
  5. groovy:000> GCStats.fullGCCount  
  6. ===> 0  
  7. groovy:000> System.gc()  
  8. ===> null  
  9. groovy:000> GCStats.fullGCCount  
  10. ===> 1  
  11. groovy:000> System.gc(); System.gc(); System.gc()  
  12. ===> null  
  13. groovy:000> GCStats.fullGCCount  
  14. ===> 4  
  15. groovy:000> GCStats.youngGCCount  
  16. ===> 9  
  17. groovy:000> System.gc(); System.gc(); System.gc()  
  18. ===> null  
  19. groovy:000> GCStats.youngGCCount  
  20. ===> 9  
  21. groovy:000> GCStats.fullGCCount  
  22. ===> 7  
  23. groovy:000> quit  

这次也顺便演示一下在Oracle JRockit R28/Windows XP SP3上跑: 

Groovysh代码  

用Java获取full GC的次数
  1. D:\>\sdk\groovy-1.7.2\bin\groovysh  
  2. Groovy Shell (1.7.2, JVM: 1.6.0_17)  
  3. Type 'help' or '\h' for help.  
  4. --------------------------------------------------  
  5. groovy:000> GCStats  
  6. ===> class GCStats  
  7. groovy:000> GCStats.youngGCCount  
  8. ===> 1  
  9. groovy:000> System.gc()  
  10. ===> null  
  11. groovy:000> GCStats.youngGCCount  
  12. ===> 2  
  13. groovy:000> System.gc(); System.gc(); System.gc()  
  14. ===> null  
  15. groovy:000> GCStats.youngGCCount  
  16. ===> 5  
  17. groovy:000> GCStats.fullGCCount  
  18. ===> 0  
  19. groovy:000> quit  

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