用Java獲取full GC的次數
阿新 • • 發佈:2019-02-19
大家如果熟悉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程式碼
也就是說,在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的辦法而已,不管了
下面用Groovy程式碼來演示一下。
使用jvmstat API需要指定vmid,對多數系統上本地Java程序的VMID就是PID。這裡正好用上以前一帖介紹的獲取程序自己的PID的方式。
Groovy程式碼
可以看到,要獲取young GC與full GC次數的讀數很簡單,找到合適的Monitor物件後,每次讀一下value屬性就好了。
在Groovy shell裡演示一下使用情況,在Sun JDK 6 update 20/Windows XP SP3上跑:
Groovysh程式碼
這次也順便演示一下在Oracle JRockit R28/Windows XP SP3上跑:
Groovysh程式碼
可以看到System.gc()引發的是young 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程式碼
-
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上,通過
在底層,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的辦法而已,不管了
下面用Groovy程式碼來演示一下。
使用jvmstat API需要指定vmid,對多數系統上本地Java程序的VMID就是PID。這裡正好用上以前一帖介紹的獲取程序自己的PID的方式。
Groovy程式碼
- 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上跑:
Groovysh程式碼
- 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上跑:
Groovysh程式碼
- 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吧? ^_^