目录
- 内存区域回顾
- 机制介绍
- 收集器介绍
- 调优
内存区域回顾
垃圾回收机制
Java 对象生命周期
根搜索算法
从GC Roots对象为起点,开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,此对象即为不可用,被判定为可回收对象。
标记过程
垃圾收集算法
堆内存区域划分
垃圾收集器
Serial - 新生代串行收集器
- 使用单线程进行垃圾回收
- 独占式
- 优点:实现简单,处理高效
- 缺点:Stop The World
- 使用-XX:+UseSerialGC指定使用
- JVM在Client模式下默认垃圾收集器
ParNew - 新生代并行收集器
- 实现和Serial相同,仅将GC线程改成多线程
- 优点:在多CPU情况下优于Serial
- 缺点:Stop The World
- -XX:+UseParNewGC
- GC线程数,-XX:ParallelGCThreads,
- 当CPU个数 < 8,ParallelGCThreads=CPU个数
- 当CPU个数 > 8,ParallelGCThreads=3 + ( (5 * CPU个数) / 8)
Parallel Scavenge - 新生代并行收集器
线程模型和ParNew相同,区别在于Parallel Scavenge收集器的目标是达到一个可以控制的吞吐量
- -XX:+UseParallelGC,指定使用
- -XX:MaxGCPauseMillis,设置最大停顿时间,大于0的整数
- -XX:GCTimeRatio,设置吞吐量大小,0~100整数,即运行用户代码时间 / 垃圾收集时间,默认值为99
- -XX:+UseAdaptiveSizePolicy 自适应GC策略开关,在自适应模式下,新生代大小,survivor和eden区的比例、晋升老年代对象年龄等参数会被自动调整,以达到最合适的停顿时间,或最大吞吐量。
Serial Old - 老年代串行收集器
- 使用标记-整理算法
- 和Serial一样是串行独占式回收器
- 可和Serial,ParNew 搭配使用
- 缺点停顿时间可能会比较长
Parallel Old - 老年代并行收集器
- 使用标记-整理算法
- 和Parallel Scavenge一样是并行多线程收集器,也是关注于吞吐量
- -XX:+UseParallelOldGC
- 缺点停顿时间可能会比较长
CMS - 老年代并发收集器
全称:Concurrent Mark Sweep
- 基于标记-清除算法
- 一种以获取最短停顿时间为目标的收集器
初始标记、重新标记仍然需要“Stop The world”。初始标记只标记GC Roots能直接关联到的对象,速度很快,时间很短。并发标记进行GC Roots Tracing,比较耗时。重新标记修正并发标记期间,程序继续运行导致的标记变动。由于整个过程中耗时最长的并发标记和并发清除阶段,GC线程可以与用户线程一起工作,总体上来说CMS是一款并发收集器,这也是CMS停顿时间较短的原因。
- -XX:+UseConcMarkSweepGC 新生代使用并行收集器,老年代使用CMS + Serial Old
几个缺点
- Cpu很敏感,并发阶段默认线程数是(CPU数量+3)/ 4 , CPU不足4个时,对程序性能影响较大
- 无法处理浮动垃圾,清理阶段,用户线程继续运行会产生新的垃圾,可能出现“Concurrent Mode Failure”失败
- CMS使用标记-清除算法,会产生大量空间碎片
优化方案
- -XX:CMSInitiatingOccupancyFraction, 设置CMS收集器老年代使用多少空间会被激活,另外当出现“Concurrent Mode Failure”时,将临时启用Serial Old收集器来重新对老年代进行收集
- -XX:+UseCMSCompactAtFullCollection, 开关参数,表示每次full GC后执行一次碎片整理。-XX:CMSFullGCsBeforeCompaction, 设置执行多少次full GC后,来一次碎片整理
垃圾收集参数总结
参考:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
两种参数格式:
- 开关参数,比如 -XX:+UseSerialGC,“+”代表开,“-”代表关
- 值参数,比如 -XX:ParallelGCThreads=4
串行回收器(Serial + Serial Old)
-XX:+UseSerialGC 在新生代和老年代中使用串行收集器 -XX:SurvivorRatio 设置Eden与Survivor区大小比例 -XX:PretenureSizeThreshold 设置大对象直接进入老年代的阈值
并行GC(ParNew + CMS + Serial Old)
- -XX:+UseParNewGC 在新生代使用并行收集器,默认老年代使用Serial Old,ParNew + Serial Old 已过期,不再推荐使用
- -XX:+UseConcMarkSweepGC 新生代使用并行收集器,老年代使用CMS + Serial Old
- -XX:ParallelGCThreads 设置用于垃圾回收的线程数
- -XX:CMSInitiatingOccupancyFraction 设置CMS收集器在老年代空间被使用多少后触发, JDK7中默认为92%, CMSInitiatingOccupancyFraction = (100 - MinHeapFreeRatio) + (CMSTriggerRatio * MinHeapFreeRatio / 100)
- -XX:+UseCMSCompactAtFullCollection 设置CMS收集器在完成垃圾收集后是否进行一次内存碎片整理,默认为true
- -XX:CMSFullGCsBeforeCompaction 设定多少次CMS垃圾回收后,进行一次内存碎片整理,默认为0
- -XX:+CMSClassUnloadingEnabled 允许对类元数据进行回收
- -XX:CMSInitiatingPermOccupancyFraction 当永久代占用率达到这个百分比时,启用CMS回收(前提先开启CMSClassUnloadingEnabled)
并行GC(Parallel Scavenge + Parallel Old)
- -XX:UseParallelGC 新生代使用Parallel Scavenge,server模式下默认开启
- -XX:UseParallelOldGC 年老代使用Parallel Old, server模式下默认开启
- -XX:ParallelGCThreads 设置用于垃圾回收的线程数
- -XX:MaxGCPauseMillis 设置最大垃圾收集停顿时间
- -XX:GCTimeRatio 设置吞吐量大小,它的值是一个0~100之间的整数,执行用户代码的时间 / 垃圾收集时间
- -XX:+UseAdaptiveSizePolicy 打开自适应GC策略
- -XX:+PrintAdaptiveSizePolicy 打印自适应分代大小调整信息
其他
- -XX:MaxTenuringThreshold 设置对象进入老年代的年龄最大值,每一次Minor GC后,对象年龄加1,默认值15,使用ConcMarkSweep时默认6
- -XX:+DisableExplicitGC 禁用显式GC,System.gc() 失效
- -XX:+PrintGCDetails 显示GC日志详细信息
- -Xloggc:<filename> 将GC日志输出到日志文件
- -Xmx 堆最大大小
- -Xms 堆初始大小
- -Xmn 新生代大小,相当于NewSize和MaxNewSize固定为相同的值
- -XX:NewSize 新生代初始大小
- -XX:MaxNewSize 新生代最大大小
- -XX:PermSize 永久代初始大小
- -XX:MaxPermSize 永久代最大大小
- -XX:+PrintFlagsFinal 打印最终的参数值
- -XX:+PrintCommandLineFlags
内存分配和回收策略
- 对象优先在Eden区分配
- 大对象直接进入老年代, -XX:PretenureSizeThreshold, 设置大对象直接进入老年代的大小阈值。只在Serial GC 和ParNew GC时有效,尽量在程序中避免大对象的存在
- 长期存活的对象进入老年代, -XX:MaxTenuringThreshold, 对象经历MinorGC的次数,默认为15,不一定非要到达这个年龄才进入老年代。
- 空间分配担保
调优
GC日志收集参数
- -verbose:gc
- -XX:+PrintGCDetails
- -XX:+PrintGCDateStamps 显示gc日志时间年月日时分秒
- -XX:+PrintGCTimeStamps 显示gc日志距启动的时长
- -Xloggc:gcfile
- -XX:+HeapDumpOnOutOfMemoryError
- -XX:HeapDumpPath
GC日志解读
Times: user - 用户态消耗的cpu时间 sys - 内核态消耗的cpu时间 real - 操作从开始到结束经过的墙钟时间
GC日志收集分析工具
- jmap
- jstat / jstatd
- jconsole
- jVisualVM
上面工具的都包含在jdk bin目录下
减少Full GC
- 增大新生代空间
- 增大survivor区大小,-XX:SurvivorRatio,默认值8
- 提高from区利用率 -XX:TargetSurvivorRatio, 默认值50
- 防止永久代满导致full GC
- 谨慎使用System.gc() // 堆外内存相关, -XX:+DisableExplicitGC --禁用
选择合适的垃圾收集器
吞吐量优先
使用ParallelGC + ParallelOldGC
降低停顿时间
使用ParNewGC + CMS,CMS对老年代的回收暂停时间远远小于ParallelOld
案例
见《jvm调优案例-xwiki吞吐量调优》