一.垃圾回收相关参数
1.-XX:+PrintGC
每次young GC或full GC打印简单日志信息,默认输出到终端
[GC (Allocation Failure) 72286K->7246K(251392K), 0.0081499 secs]
[GC (Metadata GC Threshold) 70587K->8922K(251392K), 0.0086172 secs]
[Full GC (Metadata GC Threshold) 8922K->8549K(251392K), 0.0367123 secs]
格式:[GC类型] 清理前堆使用量->清理后堆使用量(当前堆大小),清理的时间
该参数不能打印详细的GC日志,比如判断是否对象从年轻代转移老年代。
2.-XX:PrintGCDetails
详细的GC日志,一般选择开启。日志格式与选择的垃圾收集器有关。
同时使用PrintGC和PrintGCDetails,只会显示PrintGCDetails。
以下是使用PS垃圾收集器的PrintGCDetails young GC日志:
[GC (Allocation Failure) [PSYoungGen: 65536K->6747K(76288K)] 65536K->6755K(251392K), 0.0116237 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
通过以上发现:
年轻代在young GC中堆空间从65536K减到6747K,用时0.0116237 secs,使用的垃圾收集器是PS(Parallel Scavenge GC);JVM堆使用量从65536K降到6755K,总大小为251392K。同时打印消耗时间。
是否可以计算出有6755K-6747K的内存进入老年代?
以下是使用PS垃圾收集器的PrintGCDetails old GC日志:
[Full GC (Metadata GC Threshold) [PSYoungGen: 8838K->0K(76288K)] [ParOldGen: 88K->8621K(175104K)] 8926K->8621K(251392K), [Metaspace: 20551K->20551K(1067008K)], 0.0494903 secs] [Times: user=0.10 sys=0.01, real=0.05 secs]
以上各参数与young GC一致,注意user=0.10 sys=0.01, real=0.05 secs,说明使用了多线程。
3.XX:+PrintGCTimeStamps和-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps:GC时打印jvm启动至今的时间戳
-XX:+PrintGCDateStamps: GC时打印jvm启动至今的绝对日期和时间
4.-Xloggc
-Xloggc:也可以输出到指定的文件(-Xloggc:./log/gc.log)
这个参数隐式的设置了参数-XX:+PrintGC和-XX:+PrintGCTimeStamps,但建议显示指定。
二、具体的垃圾收集器分析
1.一般的虚拟机参数:
-Xmx256m -Xms256m -Dfile.encoding=UTF-8 -Dserver.port=8010 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/dump -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCDateStamps -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
2.ParNew和CMS GC
ParNew:Serial收集器的多线程版本,基于“复制算法”;
为什么选择ParNew?
运行在Server模式下的虚拟机中首选的新生代收集器,其中一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。
CMS(Concurrent Mark Sweep):以获取最短回收停顿时间为目的的收集器,基于“标记-清除”算法。
为什么选择CMS?
因为是多核处理器,并发收集、低停顿,适合web服务端应用。
3.Full/Major GC
Major GC :清理老年代
Full GC :清理整个堆空间—包括年轻代和老年代。
4.CMS垃圾收集过程:
初始标记
作用:标记老年代中所有的GC ROOT,标记被年轻代中活着的对象引用的对象。
会stop-the-world。
收集阶段,开始收集所有的GC Roots和直接引用到的对象
2019-04-09T16:20:55.865-0800: [GC (CMS Initial Mark) [1 CMS-initial-mark: 17539K(174784K)] 26422K(253440K), 0.0034858 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
格式:[老年代使用容量(新生代使用容量)]堆使用量(堆容量),时间计量
并发标记
作用:从GC root开始,遍历老年代标记所有存活的对象,与应用线程并发运行。
2019-04-09T16:20:55.869-0800: [CMS-concurrent-mark-start]
2019-04-09T16:20:55.894-0800: [CMS-concurrent-mark: 0.025/0.025 secs] [Times: user=0.05 sys=0.01, real=0.03 secs]
这里停顿了0.03秒
并发预清理
作用:并发标记在前一个阶段改变了的对象(Dirty Card),能够从dirty card对象到达的对象也会被标记。
2019-04-09T16:20:55.894-0800: [CMS-concurrent-preclean-start]
2019-04-09T16:20:55.895-0800: [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
可终止的并发预清理
这个阶段尝试着去承担STW的Final Remark阶段足够多的工作,很大程度的影响着即将来临的Final Remark的停顿。
2019-04-09T16:21:29.403-0800: [CMS-concurrent-abortable-preclean-start]
2019-04-09T16:21:34.456-0800: [CMS-concurrent-abortable-preclean: 1.684/5.053 secs] [Times: user=2.05 sys=0.04, real=5.05 secs]
重新标记
这个阶段是CMS中第二个并且是最后一个STW的阶段。该阶段的任务是完成标记整个年老代的所有的存活对象
2019-04-09T16:20:55.895-0800: [GC (CMS Final Remark) [YG occupancy: 8882 K (78656 K)]2019-04-09T16:20:55.895-0800: [Rescan (parallel) , 0.0031235 secs]2019-04-09T16:20:55.898-0800: [weak refs processing, 0.0005258 secs]2019-04-09T16:20:55.899-0800: [class unloading, 0.0041574 secs]2019-04-09T16:20:55.903-0800: [scrub symbol table, 0.0070362 secs]2019-04-09T16:20:55.910-0800: [scrub string table, 0.0005311 secs][1 CMS-remark: 17539K(174784K)] 26422K(253440K), 0.0157999 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
并发清除
和应用线程同时进行,不需要STW。这个阶段的目的就是移除那些不用的对象,回收他们占用的空间并且为将来使用。
2019-04-09T16:20:55.911-0800: [CMS-concurrent-sweep-start]
2019-04-09T16:20:55.920-0800: [CMS-concurrent-sweep: 0.009/0.009 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
并发重置
这个阶段并发执行,重新设置CMS算法内部的数据结构,准备下一个CMS生命周期的使用。
2019-04-09T16:20:55.920-0800: [CMS-concurrent-reset-start]
2019-04-09T16:20:55.920-0800: [CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
5.触发GC的时机:
Minor GC触发条件:当Eden区满时,触发Minor GC。
Full GC触发条件:
(1)调用System.gc时。
系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
(3)promotion failed
promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入老年代,而此时老年代也放不下造成的
(4)统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间
这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。
(6)堆中分配很大的对象
所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发JVM进行Full GC。
参考:
https://blog.csdn.net/chenleixing/article/details/46706039
https://blog.csdn.net/f59130/article/details/74013460
网友评论