JVM垃圾收集
博客专区 > 囚兔 的博客 > 博客详情
JVM垃圾收集
囚兔 发表于2年前
JVM垃圾收集
  • 发表于 2年前
  • 阅读 59
  • 收藏 0
  • 点赞 1
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

摘要: 近期在团队内部做的技术分享,在此记录下

目录

  1. 内存区域回顾
  2. 机制介绍
  3. 收集器介绍
  4. 调优

内存区域回顾

内存区域

垃圾回收机制

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目录下

  • GCViewer

减少Full GC

  • 将新对象预留在新生代
  1. 增大新生代空间
  2. 增大survivor区大小,-XX:SurvivorRatio,默认值8
  3. 提高from区利用率 -XX:TargetSurvivorRatio, 默认值50
  • 防止永久代满导致full GC
  • 谨慎使用System.gc() // 堆外内存相关, -XX:+DisableExplicitGC --禁用

选择合适的垃圾收集器

吞吐量优先

使用ParallelGC + ParallelOldGC

降低停顿时间

使用ParNewGC + CMS,CMS对老年代的回收暂停时间远远小于ParallelOld

案例

见《jvm调优案例-xwiki吞吐量调优》

标签: JVM GC
共有 人打赏支持
囚兔
粉丝 36
博文 66
码字总数 31745
×
囚兔
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: