第一次简单的JVM学习
此次调试源于某一个Java进程貌似有内存泄露, 用top命令查看内存发现:
启动时占用600M内存, 一天过去后涨到1.3G, 之后每天多涨200M, 到了几周后就到了峰值-Xmx4096m, 于是开始担心会不会这个应用跑着跑着outofmenory了
这个Java进程的启动参数为:
java -server -Xms4096m -Xmx4096m -Xmn1280m
-Dcom.sun.management.jmxremote.port=9910 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
-Dserverbindport=8910
-Denv_type=fat
-Djava.io.tmpdir=/home/op1/DAS/das_hotel/tmp
-Duser.dir=/home/op1/DAS/das_hotel
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump_das_hotel_10.2.6.15.hprof
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:CMSInitiatingOccupancyFraction=60
-XX:CMSTriggerRatio=70 -XX:CMSTriggerPermRatio=70
-Xloggc:/home/op1/DAS/das_hotel/logs/das_hotel_gc.log
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-classpath /home/op1/DAS/das_hotel/conf:/home/op1/DAS/das_hotel/lib/*: com.xxx.search.das.hotel.HotelDAS -t 2014-09-26-09-40-00
然后使用jstat -gc 查看, 发现FGC也就是FullGC次数是86,FGCT也就是FullGC时间消耗仅为3秒.
我一直以为FullGC是非常慢的,但看上去也很快, 一直不太了解原因.
后来查了下, 发现jstat的FGC值在垃圾回收器是CMS的时候并不表示FullGC的次数, 而是表示stop-world的次数,在CMS垃圾回收阶段, initial-mark和remark阶段会stop的world,从而增加计数.
如下面的log, 是一次把内存设为-Xmx1024m并重启后的CMS GC, 可以看到这两个时间段加起来正好是FGCT的值.
2014-09-26T10:02:38.811+0800: 1358.038: [GC [1 CMS-initial-mark: 364854K(720896K)] 382339K(1015808K), 0.0056400 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2014-09-26T10:02:38.817+0800: 1358.044: [CMS-concurrent-mark-start]
2014-09-26T10:02:38.845+0800: 1358.072: [CMS-concurrent-mark: 0.028/0.028 secs] [Times: user=0.14 sys=0.01, real=0.02 secs]
2014-09-26T10:02:38.845+0800: 1358.072: [CMS-concurrent-preclean-start]
2014-09-26T10:02:38.848+0800: 1358.075: [CMS-concurrent-preclean: 0.003/0.003 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2014-09-26T10:02:38.848+0800: 1358.075: [CMS-concurrent-abortable-preclean-start]
2014-09-26T10:02:39.150+0800: 1358.378: [CMS-concurrent-abortable-preclean: 0.108/0.302 secs] [Times: user=0.50 sys=0.01, real=0.30 secs]
2014-09-26T10:02:39.150+0800: 1358.378: [GC2014-09-26T10:02:39.150+0800: 1358.378: [ParNew: 275339K->8983K(294912K), 0.0058150 secs] 640193K->373840K(1015808K), 0.0059370 secs] [Times: user=0.04 sys=0.01, real=0.00 secs]
2014-09-26T10:02:39.157+0800: 1358.384: [GC[YG occupancy: 9253 K (294912 K)]2014-09-26T10:02:39.157+0800: 1358.384: [Rescan (parallel) , 0.0020850 secs]2014-09-26T10:02:39.159+0800: 1358.387: [weak refs processing, 0.0004540 secs]2014-09-26T10:02:39.159+0800: 1358.387: [scrub string table, 0.0006610 secs] [1 CMS-remark: 364856K(720896K)] 374110K(1015808K), 0.0036180 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
2014-09-26T10:02:39.161+0800: 1358.388: [CMS-concurrent-sweep-start]
2014-09-26T10:02:39.456+0800: 1358.683: [GC2014-09-26T10:02:39.456+0800: 1358.683: [ParNew: 271127K->3980K(294912K), 0.0036940 secs] 467083K->199936K(1015808K), 0.0038920 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
2014-09-26T10:02:39.536+0800: 1358.764: [CMS-concurrent-sweep: 0.371/0.376 secs] [Times: user=0.83 sys=0.01, real=0.38 secs]
2014-09-26T10:02:39.537+0800: 1358.764: [CMS-concurrent-reset-start]
2014-09-26T10:02:39.547+0800: 1358.774: [CMS-concurrent-reset: 0.010/0.010 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
/usr/java/jdk1.7.0_51/bin/jstat -gc 12564
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
32768.0 32768.0 0.0 1137.2 262144.0 97978.6 720896.0 28112.1 33600.0 20245.5 339 2.581 2 0.009 2.591
也就是说, 这个应用并没有频繁地调用FullGC, 这点上说明至少它运行地尚可,没出问题(即使内存占用越来越多).
但我的根本目的是调查它会不会有outofmemory的可能性.
后来我强制调用了一次FullGC, 再用jmap查看, 发现新生代老生代的数据基本上都清空了.
也就是说, 当最悲观的情况下产生了FullGC, 也能把内存清理掉而不会引发outofmenory问题.
一些简单的总结:
CMS新生代的垃圾回收算法比较简单,并不会把所有未引用的对象全释放掉, 它仅仅会释放那些栈上的对象, 对于一些复杂引用对象, 如基于socket的网络连接和流之类的, 我猜测并不会在YoungGC释放(有待考证)
老生代内存占用到了一定比例就会触发CMS GC,性能也挺快的.
当遇到CMS GC时新生代又被放满了, 就会触发FullGC, 代价很大.
后记:
这是我第一次接触到JVM的东西, 感觉还是受益颇多.
如何理解Java的各个启动参数, 如何对gc的log进行分析, 如何通过JVisualVM,JStat,JMap来对Java运行程序进行分析.
很多时候写代码稍微一个不注意,往往会对JVM的垃圾回收造成一定的影响. 所以需要在写代码的同时考虑到每行代码对JVM垃圾回收的影响.
突然有个想法:
对于C++的深入了解就是要知道每一行C++代码会被编译器怎么编译
对于Java的深入了解就是要知道每一行Java代码会被JVM怎么执行
http://rednaxelafx.iteye.com/blog/1108768
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html