JVM调优-GC调优

GC调优

GC调优指的是对JVM的垃圾回收部分进行调优,其目标是避免由垃圾回收引起的程序性能下降;

其调优的核心分为三部分

  • 通用JVM参数的设置
  • 特定垃圾回收期的JVM参数设置
  • 解决频繁的Full GC造成的性能问题
GC调优的核心指标
  • 业务吞吐量:一段时间内程序需要完成的业务数量
  • 垃圾回收吞吐量:CPU用于执行用户代码的时间与CPU总执行时间的比值
  • 延迟:用户发起一个请求到收到相应之间经历的时间
  • 内存使用量:Java程序占系统内存的最大值,在满足上面的指标的前提下,内存用量越少越好
GC调优的方法

GC调优与内存调优一样,也是分为 发现->诊断->修复->验证 四个阶段

发现阶段

我们可以通过使用jstat工具,visualvm插件,Prometheus + Grafana,观察GC日志,GC Viewer工具,GCeasy工具来帮助我们发现问题

诊断阶段
CG的正常情况

呈现锯齿状,对象创建后内存使用增加,在经过垃圾清理后大幅回落,且每次回收之后的使用内存大小接近,留存对象较少

image-20240119164533646

缓存对象过多的情况

与正常情况类似,但是在清理后的内存占用仍然较高

原因:程序中保存了大量缓存对象(临时存储在内存中的数据),导致GC无法释放,可以使用MAT或者HeapHero等工具来具体分析原因

image-20240119164733678

内存泄露

呈现锯齿状,每次GC之后的内存使用一次比一次多,最后完全无法释放内存,导致OutOfMemory错误

原因:程序中保存了大量的内存泄露对象,导致GC之后无法释放,同样可以使用MAT或者HeapHero等工具来具体分析原因

image-20240119164853182

连续的Full GC

在某个时间点出现多次Full GC,CPU使用率拉满,无法处理用户请求,一段时间后恢复正常

原因:在该时间点请求量激增,程序开始产生大量对象,同时垃圾收集速度跟不上对象创建的速度,进而导致不断地Full GC

image-20240119165258778

元空间不足导致的Full GC

发生该问题时,堆内存占用明明不高,可是还是不断的发生Full GC

原因:元空间大小不足,导致持续触发Full GC来回收元空间的数据

image-20240119165504389

修复阶段

下面介绍四种解决GC的手段,第四种方案仅建议在前三种方案无法解决时使用

1.优化JVM参数
  • 参数1:-Xmx 和 -Xms

    -Xmx 设置的是最大堆内存,需要注意的是,计算可用内存时,需要将元数据、操作系统和其他软件占用的内存排除掉。eg:服务器内存4G,操作系统+元空间最大值+其它软件占用1.5G,-Xmx可以设置为2g。

    -Xms 设置初始堆大小,这里建议将Xms和Xmx设置一样大,这样有以下优点

    运行性能好(减少堆扩容导致的性能损耗),可用性好(与同在运行的其他程序抢占内存),启动速度快(GC次数少)

    最合理的设置方式应该是根据最大并发量估算服务器的配置,然后再根据服务器配置计算最大堆内存的值。

    image-20240119170226451

  • -xx:MaxMetaspaceSize 和 -xx:MetaSpaceSice

    -XX:MaxMetaspaceSize=值 参数指的是最大元空间大小,默认值比较大,如果出现元空间内存泄漏会让操作系统可用内存不可控,建议根据测试情况设置最大值,一般设置为256m。

    -XX:MetaspaceSize=值 参数指的是到达这个值之后会触发FULLGC(网上很多文章的初始元空间大小是错误的),后续什么时候再触发JVM会自行计算。如果设置为和MaxMetaspaceSize一样大,就不会FULLGC,但是对象也无法回收。

    image-20240119170242650

  • -Xss 虚拟机栈大小

    如果我们不指定栈的大小,JVM 将创建一个具有默认大小的栈。大小取决于操作系统和计算机的体系结构。

  • 不建议手动设置的参数

    • -Xmn 年轻代的大小;在设置这个值之前需要进行大量的计算,不建议手动修改。同时如果使用G1垃圾回收器,就尽量不要使用该值,因为G1会动态地调整年轻代的大小

    • ‐XX:SurvivorRatio 伊甸园区和幸存者区的大小比例,默认值为8。

    • ‐XX:MaxTenuringThreshold 最大晋升阈值,年龄大于此值之后,会进入老年代。JVM有动态年龄判断机制:将年龄从小到大的对象占据的空间加起来,如果大于survivor区域的50%,然后把等于或大于该年龄的对象,放入到老年代,所以也不建议修改

  • 其他参数

    • -XX:+DisableExplicitGC

      禁止在代码中使用System.gc(), System.gc()可能会引起FULLGC,在代码中尽量不要使用。使用DisableExplicitGC参数可以禁止使用System.gc()方法调用

    • -XX:+HeapDumpOnOutOfMemoryError:发生OutOfMemoryError错误时,自动生成hprof内存快照文件。

      -XX:HeapDumpPath= :指定hprof文件的输出路径。

    • 打印GC日志

      JDK8及之前 : -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:文件路径

      JDK9及之后 : -Xlog:gc*:file=文件路径

  • JVM参数模版

    -Xms1g
    -Xmx1g
    -Xss256k
    -XX:MaxMetaspaceSize=512m 
    -XX:+DisableExplicitGC
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:HeapDumpPath=/opt/logs/my-service.hprof
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps
    -Xloggc:文件路径
    

    注意:

    JDK9及之后gc日志输出修改为 -Xlog:gc*:file=文件名

    堆内存大小和栈内存大小根据实际情况灵活调整。

2.减少对象产生

大多数场景下的FULLGC是由于对象产生速度过快导致的,减少对象产生可以有效的缓解FULLGC的发生

  • 对对象进行复用:重复创建和销毁对象会增加GC的负担。因此,可以尽量避免频繁创建和销毁对象,而是通过对象的复用来减少对象的产生。
  • 使用对象池:对象池是一种缓存对象的机制。通过预先创建一定数量的对象,并将其保存在池中,当需要对象时,从池中获取,使用完毕后再放回池中。这样可以避免频繁创建和销毁对象,减少GC的压力。
  • 及时释放对象引用:在对象不再使用时,及时将其引用置为null
3.更换垃圾回收器
  • 垃圾回收器的组合关系

    由于垃圾回收器分为年轻代和老年代,所以除了G1以外的垃圾处理器需要根据年轻代和老年代进行成对组合的使用

    关系图如下:

    image-20240119203154232

可以通过测试不同组合在特定负载要求下的性能表现,找到适用的垃圾处理器

4.优化垃圾回收器参数

尽量在前三种方法无效时再使用这种方法;具体需要根据产生性能问题的原因来进行针对性的排查。

参考资料

黑马JVM虚拟机入门到实战

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值