深入jvm虚拟机实战

今天有个同事问如何能通过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的次数:

深入java虚拟机 3.1 通过Java/JMX得到full GC次数?


第 75 / 121 页
于是只要知道我们用的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
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 76 / 121 页
'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
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 77 / 121 页
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
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 78 / 121 页
'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
}
}
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 79 / 121 页
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()
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 80 / 121 页
===> 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的。
关于JMX的MXBean的使用,也可以参考下面两篇文档:
Groovy and JMX
Monitoring the JVM Heap with JRuby


第 75 / 121 页
于是只要知道我们用的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
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 76 / 121 页
'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
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 77 / 121 页
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
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 78 / 121 页
'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
}
}
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 79 / 121 页
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()
深入java虚拟机 3.1 通过Java/JMX得到full GC次数?
第 80 / 121 页
===> 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的。
关于JMX的MXBean的使用,也可以参考下面两篇文档:
Groovy and JMX
Monitoring the JVM Heap with JRuby


如何更快的启动eclipse

总是感觉自己的elipse启动比别人的慢,开始以为是装的插件太多(pydev,GAE,scala……)或者是导入的项
目有点大。后来把-Xloggc:gc.log这个配置加上去看看启动的日志,吓了一跳,一次启动做了9次fullgc。和
jboss服务器一样,肯定可以优化一下配置来更少的full gc来节约启动时间。
第一次优化:把-Xms(初始化堆大小) -Xmx(JVM最大堆大小)设置为一样大小512m,避免GC后JVM重新分配内
存。但是重启eclipse的时候full gc的次数并没有减少,而且启动的时候GC全部变成了full gc,日志如下:
3.308: [Full GC 3.308: [Tenured: 0K->19530K(262144K), 0.1515426 secs] 172215K->19530K(498112K),
[Perm : 16383K->16383K(16384K)], 0.1516281 secs] [Times: user=0.14 sys=0.00, real=0.15 secs]
8.472: [Full GC 8.472: [Tenured: 19530K->34170K(262144K), 0.2060534 secs] 145021K->34170K(498112K),
[Perm : 20479K->20479K(20480K)], 0.2061412 secs] [Times: user=0.19 sys=0.00, real=0.21 secs]
9.027: [Full GC 9.027: [Tenured: 34170K->35855K(262144K), 0.1790415 secs] 54259K->35855K(498112K),
[Perm : 24575K->24575K(24576K)], 0.1791281 secs] [Times: user=0.19 sys=0.00, real=0.18 secs]
10.004: [Full GC 10.004: [Tenured: 35855K->44735K(262144K), 0.2850547 secs] 81210K->44735K(498112K),
[Perm : 28671K->28646K(28672K)], 0.2851505 secs] [Times: user=0.28 sys=0.00, real=0.28 secs]
10.725: [Full GC 10.725: [Tenured: 44735K->49542K(262144K), 0.2657311 secs] 71680K->49542K(498112K),
[Perm : 32759K->32759K(32768K)], 0.2658216 secs] [Times: user=0.25 sys=0.00, real=0.27 secs]
12.057: [Full GC 12.057: [Tenured: 49542K->64706K(262144K), 0.3637080 secs] 179985K-
>64706K(498112K), [Perm : 36863K->36863K(36864K)], 0.3637938 secs] [Times: user=0.37 sys=0.00,
real=0.36 secs]
12.788: [Full GC 12.788: [Tenured: 64706K->65640K(262144K), 0.3229940 secs] 87100K->65640K(498112K),
[Perm : 40959K->40959K(40960K)], 0.3230836 secs] [Times: user=0.31 sys=0.00, real=0.32 secs]
13.652: [Full GC 13.652: [Tenured: 65640K->70639K(262144K), 0.4553435 secs] 116918K-
>70639K(498112K), [Perm : 45055K->44963K(45056K)], 0.4554289 secs] [Times: user=0.45 sys=0.00,
real=0.46 secs]
深入java虚拟机 3.2 如何更快的启动eclipse
第 82 / 121 页
14.679: [Full GC 14.679: [Tenured: 70639K->72308K(262144K), 0.4009647 secs] 122313K-
>72308K(498112K), [Perm : 49151K->49151K(49152K)], 0.4010552 secs] [Times: user=0.38 sys=0.00,
real=0.40 secs]
从日志中分析可以看出:触发full gc的罪魁祸首是Perm,这个没有设置,所以继续优化!
第二次优化:-XX:PermSize=64m -XX:MaxPermSize=64m,把持久化的初始化大小和最大大小设置为一样。Full
gc消失了,来了24次minor gc。
0.689: [GC 0.689: [DefNew: 32256K->2724K(36288K), 0.0108873 secs] 32256K->2724K(520256K),
0.0109685 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
1.020: [GC 1.020: [DefNew: 34980K->3090K(36288K), 0.0159294 secs] 34980K->5812K(520256K),
0.0159941 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
1.451: [GC 1.451: [DefNew: 35346K->2612K(36288K), 0.0131000 secs] 38068K->8344K(520256K),
0.0131866 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2.670: [GC 2.674: [DefNew: 34868K->4032K(36288K), 0.0338445 secs] 40600K->14881K(520256K),
0.0357554 secs] [Times: user=0.03 sys=0.02, real=0.04 secs]
3.537: [GC 3.537: [DefNew: 36280K->4032K(36288K), 0.0297593 secs] 47129K->19882K(520256K),
0.0298390 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
3.595: [GC 3.595: [DefNew: 36223K->74K(36288K), 0.0121076 secs] 52074K->19924K(520256K), 0.0122015
secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
4.108: [GC 4.108: [DefNew: 32330K->1755K(36288K), 0.0071144 secs] 52180K->21605K(520256K),
0.0071898 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
7.550: [GC 7.550: [DefNew: 34011K->4032K(36288K), 0.0460676 secs] 53861K->35250K(520256K),
0.0461438 secs] [Times: user=0.05 sys=0.00, real=0.05 secs]
8.818: [GC 8.818: [DefNew: 36288K->4032K(36288K), 0.0352634 secs] 67506K->38332K(520256K),
0.0353470 secs] [Times: user=0.05 sys=0.00, real=0.04 secs]
9.926: [GC 9.926: [DefNew: 36288K->4032K(36288K), 0.0410570 secs] 70588K->45524K(520256K),
0.0411413 secs] [Times: user=0.03 sys=0.02, real=0.04 secs]
深入java虚拟机 3.2 如何更快的启动eclipse
第 83 / 121 页
10.332: [GC 10.332: [DefNew: 36288K->4031K(36288K), 0.0325734 secs] 77780K->52292K(520256K),
0.0326496 secs] [Times: user=0.05 sys=0.00, real=0.03 secs]
10.583: [GC 10.583: [DefNew: 36287K->4031K(36288K), 0.0250005 secs] 84548K->57151K(520256K),
0.0250791 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
10.765: [GC 10.765: [DefNew: 36213K->4032K(36288K), 0.0691980 secs] 89333K->72388K(520256K),
0.0692885 secs] [Times: user=0.08 sys=0.00, real=0.07 secs]
10.977: [GC 10.977: [DefNew: 36288K->4031K(36288K), 0.0426303 secs] 104644K->81872K(520256K),
0.0427115 secs] [Times: user=0.05 sys=0.00, real=0.04 secs]
11.211: [GC 11.211: [DefNew: 36287K->4032K(36288K), 0.0550659 secs] 114128K->91896K(520256K),
0.0551464 secs] [Times: user=0.03 sys=0.02, real=0.06 secs]
11.641: [GC 11.641: [DefNew: 36288K->3147K(36288K), 0.0295076 secs] 124152K->93474K(520256K),
0.0296096 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
12.591: [GC 12.591: [DefNew: 35403K->2274K(36288K), 0.0241671 secs] 125730K->95722K(520256K),
0.0242549 secs] [Times: user=0.02 sys=0.02, real=0.02 secs]
12.896: [GC 12.896: [DefNew: 34530K->3023K(36288K), 0.0193394 secs] 127978K->98567K(520256K),
0.0194275 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
13.249: [GC 13.249: [DefNew: 35280K->939K(36288K), 0.0161462 secs] 130824K->99419K(520256K),
0.0162313 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
13.919: [GC 13.919: [DefNew: 33195K->2070K(36288K), 0.0124033 secs] 131675K->100550K(520256K),
0.0125083 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
14.396: [GC 14.396: [DefNew: 34326K->4032K(36288K), 0.0204527 secs] 132806K->104239K(520256K),
0.0205335 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
14.554: [GC 14.554: [DefNew: 36288K->4031K(36288K), 0.0554755 secs] 136495K->114252K(520256K),
0.0555567 secs] [Times: user=0.06 sys=0.00, real=0.06 secs]
14.735: [GC 14.735: [DefNew: 36287K->4031K(36288K), 0.0728643 secs] 146508K->129069K(520256K),
0.0729860 secs] [Times: user=0.06 sys=0.00, real=0.07 secs]
深入java虚拟机 3.2 如何更快的启动eclipse
第 84 / 121 页
14.954: [GC 14.954: [DefNew: 36287K->4032K(36288K), 0.0529429 secs] 161325K->137308K(520256K),
0.0530283 secs] [Times: user=0.05 sys=0.00, real=0.05 secs]
15.308: [GC 15.308: [DefNew: 36288K->1126K(36288K), 0.0192389 secs] 169564K->138221K(520256K),
0.0193313 secs]
从日志中分析可以看出:频繁的minor gc是由新生代没有设置自动分配造成的。
第三次优化:-Xmn256m 设置新生代大小为256M。好了,就4次minor gc。完成任务。日志如下:
3.592: [GC 3.592: [DefNew: 209792K->19904K(235968K), 0.0765218 secs] 209792K->19904K(498112K),
0.0766072 secs] [Times: user=0.06 sys=0.02, real=0.08 secs]
10.457: [GC 10.457: [DefNew: 229696K->26176K(235968K), 0.1996293 secs] 229696K->58203K(498112K),
0.1997121 secs] [Times: user=0.17 sys=0.03, real=0.20 secs]
12.862: [GC 12.862: [DefNew: 235968K->17131K(235968K), 0.1315169 secs] 267995K->74647K(498112K),
0.1315965 secs] [Times: user=0.14 sys=0.00, real=0.13 secs]
14.465: [GC 14.465: [DefNew: 226923K->26176K(235968K), 0.1363962 secs] 284439K->101396K(498112K),
0.1364835 secs]
最后的配置如下:
-Xmn128m
-Xms512m
-Xmx512m
-XX:PermSize=64m
-XX:MaxPermSize=64m
-verbose:gc
-XX:+PrintGCTimeStamps
深入java虚拟机 3.2 如何更快的启动eclipse
第 85 / 121 页
-XX:+PrintGCDetails
-Xloggc:gc.log



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值