JVM垃圾回收器的了解及选择

本文深入探讨了JVM的堆配置,包括-Xmx、-Xms等参数,以及CMS、Parallel Scavenge和G1等垃圾回收器的工作原理和配置。解释了为何采用分代收集策略,分析了不同回收器的优缺点,并提供了如何根据系统需求选择合适垃圾回收器的建议。同时,还介绍了ZGC的特性和低延迟目标。
摘要由CSDN通过智能技术生成

堆配置:

-Xmx

  • 堆大小
  • 可取值:1024k的倍数,必须 >2M
  • 等价于:-XX:MaxHeapSize

-Xms

  • 堆初始大小
  • 可取值:1024k的倍数,必须 >1M
  • 等于 新生代和老年代之和

-Xmn

  • 新生代最大值、初始值
  • 等价于:-XX:NewSize(初始值)、-XX:MaxNewSize(最大值)

-Xss

  • 线程栈大小
  • 默认值Linux(64-bit):1M
  • 等价于-XX:ThreadStackSize

-XX:NewRatio

  • young/old
  • 默认2

-XX:MaxDirectMemorySize

  • New I/O size
  • 默认值0

-XX:MaxMetaspaceSize

  • 元空间最大值
  • 默认无限制,只受内存空间大小的限制

-XX:MaxTenuringThreshold

  • 最大老年代转化年纪
  • 默认值:parallel GC为15,CMS为6

-XX:SurvivorRatioratio**

  • 默认8,计算公式 Sur=Y/(Ratio+2)

-XX:+UseTLAB

  • TLAB:线程本地缓冲区,用于堆中对象内存分配
  • -XX:TLABSize,设置大小

垃圾回收器算法

复制算法标记清除标记整理
指针调整需要不需要需要
效率高效
运行简单
略低
两遍扫描(标记+清除)
最低
两遍扫描(标记+整理)
适用新生代(Eden+Sur)老年代(老年代+方法区)老年代(老年代+方法区+H块)
空间利用率50%100%100%
缺点空间利用率低有内存碎片执行较复杂
解决方案新生代8:1:1
空间利用率和效率的最佳权衡
标记整理
内存碎片有碎片

为什么采用分代收集?

堆的不同分代,对象的生存周期具有不同的特点。针对不同分代中的对象分别的采用不同的算法,朝生夕死的对象采用复制算法;长期存活的对象采用标记清除、标记整理算法。

  • 有效的提高了不同分代的清除效率
  • 将三种收集算法进行有效组合,充分利用每种算法的优势

分代回收器对比

新生代

SerialParallel ScavengerParNewG1
设置-XX:+UseSerialGC-XX:+UseParallelGC
自动 -XX:+UseParallelOldGC
显式 -XX:-UseParallelOldGC为
Serial Old
-XX:+UseParNewGC
只能和-XX:+UseConcMarkSweepGC使用
-XX:+UseG1GC
老年代Serial OldParallel Old & Serial OldCMS ( Serial Old 在JDK8弃用)G1
算法复制算法复制算法复制算法复制算法 & 标记整理
类型单线程
常用于客户端
多线程
JDK8默认回收器
可用于客户端、服务器
多线程多线程
适用几十M~几百M几百M~几个G几个G~几十G十几G~一百多G
缺陷STW耗时长可停顿时间设置很鸡肋
解决使用parallel GC使用CMS或G1

老年代

Serial OldParallel OldCMSG1
设置-XX:+UseSerialGC-XX:+UseParallelOldGC
自动-XX:+UseParallelGC
-XX:+UseConcMarkSweepGC
只能和ParNew一起
-XX:+UseG1GC
类型单线程多线程多线程多线程
算法标记整理标记整理标记清除复制算法 & 标记整理
效率较高
内存利用率100%100%100%O、H块:100%
E、S块:< 100%
缺点CPU敏感
有浮动垃圾
每个region都有不能存储对象的内存,内存大小要求较高
解决方案浮动垃圾:提前触发回收
现在的硬件CPU核心数已不是问题
使用G1回收器
适用几十M~几百M几百M~几个G几个G~几十G十几G~一百多G
目标解决Parallel GC的追求吞吐量基础上的高延迟问题可预测可控制回收模型

分代回收器具体参数配置:

CMS:

配置

提前触发CMS回收:
  • -XX:CMSTriggerRatio,默认75%
并发可中断预处理:
  • -XX:+CMSScavengeBeforeRemark,默认禁用

解决漏标:

漏标的产生是在并发标记过程,用户线程并发的产生新的垃圾。解决在重新标记Remark阶段停顿标记漏标的垃圾
算法:Incremental update ,通过引用增加的改变,从根开始再扫描。(关注引用增加)

重新标记阶段只扫描dirty的对象,dirty是在并发预处理阶段完成的。

  • 并发预处理:将漏标对象标记为dirty状态,缩减remark的扫描范围
  • 并发可中断预处理:默认禁用。做些remark的工作,尽可能进行一次YGC减少remark的扫描时间

新对象避免标记:

一个统一的区域记录新对象,以避免被标记

并发线程数:

  • -XX:ConcGCThreads

默认不处理永久代垃圾回收

  • -XX:CMSPermGenSweepingEnabled,开启永久代回收
  • -XX:CMSInitiatingPermOccupancyFraction,永久代触发回收百分比阈值,默认80%
  • -XX:CMSClassUnloadingEnabled,回收时可对class进行卸载

Parallel Scanvenge GC

自适应堆大小:

-XX:+UseAdaptiveSizePolicy

  • 默认开启
  • 为求满足最大停顿时间吞吐量要求,会自动适当减小、增加堆大小

设置最大停顿时间:

-XX:MaxGCPauseMillis=milliseconds

  • 默认无最大限制
  • 不建议设置。因为若设置的小,GC的频率会提升,并没有针对GC的选择性和优化

追求最大吞吐量

  • -XX:GCTimeRatio=,默认值99
  • 吞吐量计算: / (1 + ),N越大,吞吐量越大

并发回收线程数:

-XX:ParallelGCThreads

  • 默认由核心数参考决定

并行压缩??

  • parallel compaction:并行压缩比单个线程做major回收更具有扩展性,可设置并发线程数
  • -XX:+UseParallelGC配置,默认开启
  • -XX:-UseParallelOldGC配置,默认关闭

默认堆大小:

  • Client:
    • 堆最大值:
      • 物理内存> 192M,1/2物理内存
      • 物理内存> 1G,1/4物理内存
    • 堆初始值:
      • 1/64物理内存,不小于8M
    • 新生代大小:
      • 1/3堆大小
  • Server:
    • 堆最大值:
      • 物理内存>4G,1/4的物理内存
    • 其他,与Client类似

过程:

  1. 先符合最大停顿时间,超过则自适应减小堆大小
  2. 再满足吞吐量,不满足则自适应增大堆大小
  3. 最后,在以上两点满足情况下,自适应减小堆大小(最大限值由-Xmx设置)

G1:

配置

region大小设置:
  • -XX:G1HeapRegionSize,默认在heap大小基础上的工效学决定
  • 可取值:1 MB ~ 32 MB
  • 目标是创建2048个region在堆中
设置最大停顿时间:
  • -XX:MaxGCPauseMillis=milliseconds
  • 默认200,指200ms
  • 建议设置。G1会根据回收价值优先筛选回收,并有一些优化方法
分配担保空间预留:
  • -XX:G1ReservePercent,默认10。指10%
触发百分比阈值:
  • -XX:InitiatingHeapOccupancyPercent,默认45。指45%
  • 堆占比达到45%,开启GC
并发线程数:
  1. -XX:ParallelGCThreads=n
    • STW的工作线程数
    • 可取值:
      • 逻辑线程数 <= 8,逻辑线程数
      • 逻辑线程数 > 8,5/8逻辑线程数
  2. -XX:ConcGCThreads=m
    • 并发标记工作线程数
    • 可取值:
      • 1/4的ParallelGCThreads

解决漏标

产生原理和CMS相同,都是在并发标记阶段由用户线程产生的新的垃圾。解决由SATB(snapshot at the beginning)算法比对识别漏标

  • SATB存储在每个region块的头部

新对象避免被标记

产生原因同漏标,解决由TAMS()记录每个region的新对象

  • TAMS存储在每个region块的头部

region间引用避免全扫

在每个region中的Remembered Set log 存储其它块中相关联的对象引用

永久代(JDK1.7) & 元空间(JDK1.8) 的回收

G1中只在此空间满时进行Full GC回收,其他情况是不对永久代、元空间进行回收的

大对象

在G1中,如果一个对象的内存占用超过region的一半,那就被看作是大对象

优势

  • 可适用更大范围堆内存的回收
  • 将堆拆分为E、S、O、H不同区块,建立可预测可控制的停顿时间模型。程序允许的时间范围内,获得更高效的回收结果
  • 筛选回收:根据不同区块的回收价值,设置不同的回收优先级
  • 解决了CMS的内存碎片问题
  • 解决了Parallel GC的停顿时间比较鸡肋的问题

ZGC

ZGC Feature

  • 类似G1,但无分代概念
  • 可预测低延迟并发垃圾回收器:Z Garbage Collector
  • 平台支持:Linux从JDK11开始,MacOS从JDK14开始
  • 目前是试验版本,非商业版本
  • 回收目标:
    • 适用从8M到16T范围的堆回收
    • 停顿时间10ms以内,只有几毫秒
    • 停顿时间不会随着堆大小、存活数量的增加而增加。只与GCRoots有关
  • 功能特点:
    • [Returning Unused Memory to the Operating System]
    • 支持Linux大页面:大页面通常会产生更好的性能(在吞吐量、延迟和启动时间方面),并且没有真正的缺点,只是它的设置稍微复杂一些
    • Transparent Huge Pages On Linux
    • 支持非一致性内存访问:NUMA 全称 Non-Uniform Memory Access
    • 使用有色指针(要求内存小于4TB,效率非常高)
    • 使用加载屏障

Large Page支持:

  • Linux在kernel 2.6以后支持
  • 查看是否支持:cat /proc/meminfo | grep Huge
  • 大页面:通过优化Translation-Lookaside Buffers(翻译-查看缓冲区)来减少多次内存访问从而提高内存读取性能。缺点是需要更大的内存页大小,可能会挤占其他应用造成其他应用分页过多从而减慢整个系统的速度

NUMA:

  • -XX:+UseNUMA, 默认禁用
  • 只有在ZGC 和 parallel GC时支持
  • NUMA:通过机器的非一致内存架构来减少应用的内存延迟,拆分内存并不强制内存一致性。

如何选择垃圾回收器?

  1. 先让JVM自己去适配一个垃圾回收器。因为JVM会根据系统的硬件条件去自动选择。

  2. 如果JVM自动选择的垃圾回收器不能满足性能要求,再尝试调整堆大小及堆中的新、老分区比例。若依然不能满足再去选择回收器

  3. 先选择新生代回收器:因为新生代的垃圾回收器是应用程序中最常使用到的,并且新生代的垃圾回收器的选择往往决定了老年代的垃圾回收器选择。

    参考维度:单或多用户应用、数据集级别、机器的单或多处理器和线程的硬件条件、系统吞吐量、响应速度要求

    新生代的三个垃圾回收器具有不同运行、性能特点:

    • serial GC:适用单用户、小数据集(100M以内)、单处理器机器;
      • 串行GC没有多线程通信开销,单线程或单用户应用中有很好的性能表现
      • 多处理器机器也能使用,但数据集一定不能太大,在100M最好
    • parallel GC:并发回收器,适用中、大型数据集
      • 适用于多用户、多线程、多处理器,数据集较大,注重系统吞吐量
      • 并行回收,同serial GC无区别,只是serial GC的多线程版本
      • 可控制停顿时间和系统吞吐量,堆大小做自适应性调整
    • CMS、G1:并发回收器,适用数据集更加广泛达几十G,更注重系统响应速度
      • 适用于多用户、多线程、多处理器,数据集大
      • 并发回收,不同于serial GC,很短的停顿时间

JVM 自适配收集器过程:

  1. JVM 首先根据不同的硬件条件(处理器、内存大小、操作系统)自适应判断适用客户端模式运行时编译器服务器模式运行时编译器
  2. 再根据不同的模式选择适用的垃圾收集器
  3. 判断为服务器模式运行时编译器 模式的基础条件有:
    • 大于等于两个物理处理器
    • 大于等于2GB物理内存

现在jdk1.8中,确定为服务器模式运行时编译器 模式一般会默认parallel GC收集器,具体堆大小会依据具体的硬件情况不同而不同。(可参考parallel GC的默认值)

Concurrent mode failed

并发模式失败,发生在回收器的并发阶段,由于并发、内存不足、显示调用GC、诊断工具使用等原因导致垃圾回收被中断,从而发生并发模式失败。

发生回收器:

CMS、G1

如何解决:

  • 调整堆的大小和分区比例
  • 修改提前触发堆百分比

handler promotion failed

每次回收,都会发生新生代对象向老年代晋升的现象,所以会有晋升失败的可能。

具体判断过程

在这里插入图片描述

system.gc()

不建议在生产中使用,甚至在循环中应用。可能会导致并发模式失败。

可禁用:-XX:+DisableExplicitGC。由JVM决定回收的必要性。

OOM相关配置:

-XX:+HeapDumpOnOutOfMemoryError:

  • 默认禁用

-XX:HeapDumpPath=path

  • 默认当前工作路径
  • -XX:HeapDumpPath=/var/log/java/java_heapdump.hprof

-XX:LogFile=path

  • 默认当前工作路径

查看默认参数值

java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize

例如:
➜  ~ java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
    uintx MaxHeapSize                              := 2147483648                          {product}
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)



Q & A

  • CMS可以和Serial配合吗?

    不可以,JDK8已经禁用了和Serial的配合

为什么要学JVM1、一切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。3、同时,JVM又是各大软件公司笔试、面试的重中之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之一。4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。课程介绍本课程包含11个大章节,总计102课时,无论是笔试、面试,还是日常工作,可以让您游刃有余。第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使用。第3章 类的加载、解释、编译,本章节带你深入理解类加载的分类、范围、双亲委托策略,自己手写类加载,理解字节码解释、即时编译、混合模式、热点代码检测、分层编译等核心知识。第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数、虚拟机栈、本地方法栈、方法区、永久代、元空间等全部内容。第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全区,四种引用类型的使用和区别等等。第7章 GC算法与收集,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收的原理和使用,垃圾回收的组合及分代收集等。第8章 GC日志详解,各种垃圾回收的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使用。第10章 阿里巴巴Arthas在线诊断工具,这是一个特别小惊喜,教您怎样使用当前最火热的arthas调优工具,在线诊断各种JVM问题。第11章 故障排除,本章会使用实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。课程资料课程附带配套项目源码2个159页高清PDF理论篇课件1份89页高清PDF实战篇课件1份Unsafe源码PDF课件1份class_stats字段说明PDF文件1份jcmd Thread.print解析说明文件1份JProfiler内存工具说明文件1份字节码可视化解析工具1份GC日志可视化工具1份命令行工具cmder 1份学习方法理论篇部分推荐每天学习2课时,可以在公交地铁上用手机进行学习。实战篇部分推荐对照视频,使用配套源码,一边练习一遍学习。课程内容较多,不要一次性学太多,而是要循序渐进,坚持学习。      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值