`
RednaxelaFX
  • 浏览: 3006321 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

通过Java/JMX得到full GC次数?

阅读更多
今天有个同事问如何能通过JMX获取到某个Java进程的full GC次数:
引用
hi,问个问题,怎们在java中获取到full gc的次数呢?
我现在用jmx的那个得到了gc次数,不过不能细化出来full gc的次数

for (final GarbageCollectorMXBean garbageCollector
        : ManagementFactory.getGarbageCollectorMXBeans()) {
    gcCounts += garbageCollector.getCollectionCount();
}

你比如我现在是这样拿次数的

我回答说因为full GC概念只有在分代式GC的上下文中才存在,而JVM并不强制要求GC使用分代式实现,所以JMX提供的标准MXBean API里不提供“full GC次数”这样的方法也正常。
既然“full GC”本来就是非常平台相关的概念,那就hack一点,用平台相关的代码来解决问题好了。这些GC的MXBean都是有名字的,而主流的JVM的GC名字相对稳定,非要通过JMX得到full GC次数的话,用名字来判断一下就好了。

举个例子来看看。通过JDK 6自带的JConsole工具来查看相关的MXBean的话,可以看到,

GC的MXBean在这个位置:


这个例子是用server模式启动JConsole的,使用的是ParallelScavenge GC,它的年老代对应的收集器在这里:


该收集器的总收集次数在此,这也就是full GC的次数:


于是只要知道我们用的JVM提供的GC MXBean的名字与分代的关系,就可以知道full GC的次数了。
Java代码写起来冗长,这帖就不用Java来写例子了,反正API是一样的,意思能表达清楚就OK。
用一个Groovy脚本简单演示一下适用于Oracle (Sun) HotSpot与Oracle (BEA) JRockit的GC统计程序:
import java.lang.management.ManagementFactory

printGCStats = {
  def youngGenCollectorNames = [
    // Oracle (Sun) HotSpot
    // -XX:+UseSerialGC
    'Copy',
    // -XX:+UseParNewGC
    'ParNew',
    // -XX:+UseParallelGC
    'PS Scavenge',
    
    // Oracle (BEA) JRockit
    // -XgcPrio:pausetime
    'Garbage collection optimized for short pausetimes Young Collector',
    // -XgcPrio:throughput
    'Garbage collection optimized for throughput Young Collector',
    // -XgcPrio:deterministic
    'Garbage collection optimized for deterministic pausetimes Young Collector'
  ]

  def oldGenCollectorNames = [
    // Oracle (Sun) HotSpot
    // -XX:+UseSerialGC
    'MarkSweepCompact',
    // -XX:+UseParallelGC and (-XX:+UseParallelOldGC or -XX:+UseParallelOldGCCompacting)
    'PS MarkSweep',
    // -XX:+UseConcMarkSweepGC
    'ConcurrentMarkSweep',
    
    // Oracle (BEA) JRockit
    // -XgcPrio:pausetime
    'Garbage collection optimized for short pausetimes Old Collector',
    // -XgcPrio:throughput
    'Garbage collection optimized for throughput Old Collector',
    // -XgcPrio:deterministic
    'Garbage collection optimized for deterministic pausetimes Old Collector'
  ]

  R: {
    ManagementFactory.garbageCollectorMXBeans.each {
      def name  = it.name
      def count = it.collectionCount
      def gcType;
      switch (name) {
      case youngGenCollectorNames:
        gcType = 'Minor Collection'
        break
      case oldGenCollectorNames:
        gcType = 'Major Collection'
        break
      default:
        gcType = 'Unknown Collection Type'
        break
      }
      println "$count <- $gcType: $name"
    }
  }
}

printGCStats()

执行可以看到类似这样的输出:
5 <- Minor Collection: Copy
0 <- Major Collection: MarkSweepCompact

↑这是用client模式的HotSpot执行得到的;
0 <- Minor Collection: Garbage collection optimized for throughput Young Collector
0 <- Major Collection: Garbage collection optimized for throughput Old Collector

↑这是用JRockit R28在32位Windows上的默认模式得到的。

通过上述方法,要包装起来方便以后使用的话也很简单,例如下面Groovy程序:
import java.lang.management.ManagementFactory

class GCStats {
  static final List<String> YoungGenCollectorNames = [
    // Oracle (Sun) HotSpot
    // -XX:+UseSerialGC
    'Copy',
    // -XX:+UseParNewGC
    'ParNew',
    // -XX:+UseParallelGC
    'PS Scavenge',
    
    // Oracle (BEA) JRockit
    // -XgcPrio:pausetime
    'Garbage collection optimized for short pausetimes Young Collector',
    // -XgcPrio:throughput
    'Garbage collection optimized for throughput Young Collector',
    // -XgcPrio:deterministic
    'Garbage collection optimized for deterministic pausetimes Young Collector'
  ]
  
  static final List<String> OldGenCollectorNames = [
    // Oracle (Sun) HotSpot
    // -XX:+UseSerialGC
    'MarkSweepCompact',
    // -XX:+UseParallelGC and (-XX:+UseParallelOldGC or -XX:+UseParallelOldGCCompacting)
    'PS MarkSweep',
    // -XX:+UseConcMarkSweepGC
    'ConcurrentMarkSweep',
    
    // Oracle (BEA) JRockit
    // -XgcPrio:pausetime
    'Garbage collection optimized for short pausetimes Old Collector',
    // -XgcPrio:throughput
    'Garbage collection optimized for throughput Old Collector',
    // -XgcPrio:deterministic
    'Garbage collection optimized for deterministic pausetimes Old Collector'
  ]
  
  static int getYoungGCCount() {
    ManagementFactory.garbageCollectorMXBeans.inject(0) { youngGCCount, gc ->
      if (YoungGenCollectorNames.contains(gc.name))
        youngGCCount + gc.collectionCount
      else
        youngGCCount
    }
  }
  
  static int getFullGCCount() {
    ManagementFactory.garbageCollectorMXBeans.inject(0) { fullGCCount, gc ->
      if (OldGenCollectorNames.contains(gc.name))
        fullGCCount + gc.collectionCount
      else
        fullGCCount
    }
  }
}

用的时候:
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()
===> null
groovy:000> System.gc()
===> null
groovy:000> GCStats.fullGCCount
===> 3
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> System.gc()
===> null
groovy:000> GCStats.youngGCCount
===> 9
groovy:000> GCStats.fullGCCount
===> 4
groovy:000> quit

这是在Sun JDK 6 update 20上跑的。顺带一提,如果这是跑在JRockit上的话,那full GC的次数就不会增加——因为JRockit里System.gc()默认是触发young GC的;请不要因为Sun HotSpot的默认行为而认为System.gc()总是会触发full GC的。

Poonam Bajaj以前也写过一篇blog提到HotSpot VM里的GC MBean的名字的:
Collector names for GarbageCollectorMXBean MXBean

关于JMX的MXBean的使用,也可以参考下面两篇文档:
Groovy and JMX
Monitoring the JVM Heap with JRuby
  • 大小: 76.9 KB
  • 大小: 98.4 KB
  • 大小: 107.5 KB
分享到:
评论
15 楼 chainhou 2013-05-27  
RednaxelaFX 写道
chainhou 写道
你好,想问个问题,我以-server启动应用,jConsole中的Garbage Collector是java.lang:type=GarbageCollector,name=PS MarkSweep和java.lang:type=GarbageCollector,name=PS Scavenge,不以-server启动,就是java.lang:type=GarbageCollector,name=MarkSweepCompact和java.lang:type=GarbageCollector,name=Copy,而我启动参数设置了-XX:+UseConcMarkSweepGC后,又是java.lang:type=GarbageCollector,name=ConcurrentMarkSweep,那这个负责full gc的收集器是以哪个为准,也就是怎样知道是哪个收集器负责full gc,从而获取full gc的次数?当然,人工通过visualVm和jconsole比对是可以知道的,但如果程序中就不好这样做了。

HotSpot VM现有的实现的话old gen只有一个,负责full GC/major GC的collector也只有一个。所以每次运行就只会有一个GarbageCollectorMXBean对应major GC。这帖里说的那些名字都试一次最多也只会得到1个结果。那个就是了。
,难道是要用穷举法?
哈哈,那我只得把所有的GarbageCollector都列出来,做为ObjectName,一个个试了。ps:谢谢你的回复
14 楼 RednaxelaFX 2013-05-27  
chainhou 写道
你好,想问个问题,我以-server启动应用,jConsole中的Garbage Collector是java.lang:type=GarbageCollector,name=PS MarkSweep和java.lang:type=GarbageCollector,name=PS Scavenge,不以-server启动,就是java.lang:type=GarbageCollector,name=MarkSweepCompact和java.lang:type=GarbageCollector,name=Copy,而我启动参数设置了-XX:+UseConcMarkSweepGC后,又是java.lang:type=GarbageCollector,name=ConcurrentMarkSweep,那这个负责full gc的收集器是以哪个为准,也就是怎样知道是哪个收集器负责full gc,从而获取full gc的次数?当然,人工通过visualVm和jconsole比对是可以知道的,但如果程序中就不好这样做了。

HotSpot VM现有的实现的话old gen只有一个,负责full GC/major GC的collector也只有一个。所以每次运行就只会有一个GarbageCollectorMXBean对应major GC。这帖里说的那些名字都试一次最多也只会得到1个结果。那个就是了。
13 楼 chainhou 2013-05-27  
你好,想问个问题,我以-server启动应用,jConsole中的Garbage Collector是java.lang:type=GarbageCollector,name=PS MarkSweep和java.lang:type=GarbageCollector,name=PS Scavenge,不以-server启动,就是java.lang:type=GarbageCollector,name=MarkSweepCompact和java.lang:type=GarbageCollector,name=Copy,而我启动参数设置了-XX:+UseConcMarkSweepGC后,又是java.lang:type=GarbageCollector,name=ConcurrentMarkSweep,那这个负责full gc的收集器是以哪个为准,也就是怎样知道是哪个收集器负责full gc,从而获取full gc的次数?当然,人工通过visualVm和jconsole比对是可以知道的,但如果程序中就不好这样做了。
12 楼 RednaxelaFX 2011-09-03  
daly1987 写道
应该是jconsole -J-server ????

貌似直接-server不可以啊

我在JDK6上一直用jconsole -server都没问题。如果你遇到问题那请详细说说是怎么个不行法。
11 楼 daly1987 2011-09-03  
应该是jconsole -J-server ????

貌似直接-server不可以啊
10 楼 RednaxelaFX 2011-08-18  
daly1987 写道
用server模式启动JConsole???

怎么启动,有什么参数,我查文档没有啊

jconsole -server
9 楼 daly1987 2011-08-18  
用server模式启动JConsole???

怎么启动,有什么参数,我查文档没有啊
8 楼 rain2005 2010-12-17  
在分布式java应用与实践提到了猛男哦,我也是看了这本书才看一下虚拟机的哦,jstat,pidstat,jVisualVM够用了哦。其他的什么jstack还要看看。
7 楼 RednaxelaFX 2010-11-27  
yznxing 写道
是OLD区的gc 策略,我其实是想表达对应的full gc采用的收集器,根据收集器名称来找到full gc次数。

是,这个完全正解。
yznxing 写道
我现在脑海里面一直闪现的是 permGen内存不足,触发FULL
GC~~~

HotSpot的PermGen内存不足的时候确实也会触发full GC就是了…
6 楼 yznxing 2010-11-27  
我现在脑海里面一直闪现的是 permGen内存不足,触发FULL
GC~~~
5 楼 yznxing 2010-11-27  
RednaxelaFX 写道
yznxing 写道
只要明确permgen区的 gc 策略,就可以通过jmx来获取对应的次数了吧。

perm...gen?这个上下文我没看出跟PermGen的关系是什么,求解


我勒个去。。我正在搞pergGen的测试,一不留神就说出来了。
是OLD区的gc 策略,我其实是想表达对应的full gc采用的收集器,根据收集器名称来找到full gc次数。
4 楼 RednaxelaFX 2010-11-27  
yznxing 写道
只要明确permgen区的 gc 策略,就可以通过jmx来获取对应的次数了吧。

perm...gen?这个上下文我没看出跟PermGen的关系是什么,求解
3 楼 yznxing 2010-11-27  
只要明确permgen区的 gc 策略,就可以通过jmx来获取对应的次数了吧。

下面是我测试获得的,的确是一样的,他们监控jboss的也是通过这种方式。
比较WS但是很简单,有效。

GC:[PS Scavenge: Count=142 GCTime=0.1470sec][PS MarkSweep: Count=45 GCTime=0.8160sec]

2 楼 RednaxelaFX 2010-10-21  
gaoerrong 写道
呵呵,按照这个方式和gclog中记录的full gc次数和时间完全一样。

嗯,一致就对了
本来如果只是要知道某个Java进程的GC次数和耗时统计,我会推荐用jstat来做。但这次同事问的是“如何通过JMX获取”,就给了上面的办法
1 楼 gaoerrong 2010-10-21  
呵呵,按照这个方式和gclog中记录的full gc次数和时间完全一样。

相关推荐

Global site tag (gtag.js) - Google Analytics