jvm性能调优实战 - 35电商APP后台系统如何对Full GC进行深度优化

简介: jvm性能调优实战 - 35电商APP后台系统如何对Full GC进行深度优化

业务背景

这个JVM性能优化的案例,很多核心的思想其实也跟之前是相同的,只不过在优化的过程中会带出来一些比较高级的参数的调优。

业务背景,这是一个垂直电商公司,一些细分领域的电商业务.

注册用户量有就数百万的规模,每日活跃用户数量几十万,每天APP的整体请求量也就小几千万的级别,也并不大。高峰期的QPS也就每秒数百请求

但即使如此的一个普通APP的后台系统,感觉上压力一点儿都不大,是不是真的就没有JVM的性能问题呢?

当然不是了,这个APP虽然不大,但是他同样有JVM相关的性能问题,而且也需要一些细致的优化才可以。


JVM性能问题

部署生产环境的时候往往就不会对JVM进行什么参数的设置,可能很多时候就是用一些默认的JVM参数。

默认的JVM参数绝对是系统负载逐渐增高的时候一个最大的问题 . 前期是没什么问题,但是中后期开始,当有一定用户量,有一定负载了,此时就会出现一些问题了。

新生代内存过小,会导致Survivor区域内存过小,同时Eden区域也很小。

Eden区域过小,自然会导致频繁的触发Young GC,Survivor区域过小,自然会导致经常在Young GC之后存活对象其实也没多少,但就是Survivor区域放不下。

此时必然会导致对象经常进入老年代中,因此也必然会导致老年代过一段时间就放满了,然后就会触发Full GC。

所以当时这个垂直电商APP的各个系统通过jstat分析JVM GC之后发现,基本上高峰期的时候,Full GC每小时都会发生好几次。

Full GC一般在正常情况下,都是以天为单位发生的,比如每天发生一次,或者是几天发生一次Full GC。

要是每小时都发生几次Full GC,那么就会导致系统每小时都卡顿好几次。这个时候必然是不行的。

分析系统情况过后,定制了一套公司级别的JVM参数模板

在大部分工程师都对JVM优化不是很精通的情况下,通过推行一个JVM参数模板,让各个系统短时间内迅速就优化了JVM的性能。


如果你是一个团队的leader,或者是一个中小型公司的架构师,那么必然是需要为团队或者公司定制一套基本的JVM参数模板的

然后尽量让大部分系统套用这个模板,基本保证JVM性能别太差,避免很多初中级工程师直接使用默认的JVM参数,可能一台8G内存的机器上,JVM堆内存就分配了几百MB。

下面定制出来的适合他们公司的JVM参数模板:

-Xms4096M -Xmx4096M -Xmn3072M -Xss1M  -XX:PermSize=256M -XX:MaxPermSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0

为什么如此定制JVM参数模板呢?

首先,8G的机器上给JVM堆内存分配4G就差不多了,毕竟可能还有其他进程会使用内存,一般别让JVM堆内存把机器内存给占满。

然后年轻代给到3G,之所以给到3G的内存空间,就是因为让年轻代尽量大一些,进而让每个Survivor区域都达到300MB左右。

根据当时对这个业务系统的分析,假设用默认的JVM参数,可能年轻代就几百MB的内存,Survivor区域就几十MB的内存

那么每次垃圾回收过后存活对象可能会有几十MB,这是因为在垃圾回收的一瞬间可能有部分请求没处理完毕,此时会有几十MB对象是存活的,所以很容易触发动态年龄判定规则,让部分对象进入老年代。

所以在分析过后,给年轻代更大内存空间,让Survivor空间更大,这样在Young GC的时候,这一瞬间可能有部分请求没处理完毕,有几十MB的存活对象,这个时候在几百MB的Survivor空间中可以轻松放下,绝对不会进老年代。

基本上在这个内存分配之下,对于这个垂直电商APP的大部分后台业务系统,都是可以轻松hold住的

不同的系统运行时的情况略有不同,但是基本上都是在每次Young GC过后存活几MB~几十MB的对象,所以此时在这个参数模板下,都可以抗住。

只要把内存分配完毕,那么对象进入老年代的速度是极慢极慢的,经过这个参数模板 ,通过jstat观察,基本上发现各个系统的Full GC都变成了几天才会发生一次。

此时在参数模板里还会加入Compaction相关的参数,保证每次Full GC之后都会执行一次压缩,解决内存碎片的问题。


如何优化每次Full GC的性能?

再介绍一下优化的时候调整的另外两个参数,这个两个参数可以帮助优化FUll GC的性能,把每次Full GC的时间进一步降低一些。

  • 一个参数是“-XX:+CMSParallelInitialMarkEnabled”,这个参数会在CMS垃圾回收器的“初始标记”阶段开启多线程并发执行。

大家应该还记得初始标记阶段,是会进行Stop the World的,会导致系统停顿,所以这个阶段开启多线程并发之后,可以尽可能优化这个阶段的性能,减少Stop the World的时间。

  • 另外一个参数是“-XX:+CMSScavengeBeforeRemark”,这个参数会在CMS的重新标记阶段之前,先尽量执行一次Young GC。

这样做有什么作用呢?

其实大家都记得,CMS的重新标记也是会Stop the World的,所以所以如果在重新标记之前,先执行一次Young GC,就会回收掉一些年轻代里没有人引用的对象。

所以如果先提前回收掉一些对象,那么在CMS的重新标记阶段就可以少扫描一些对象,此时就可以提升CMS的重新标记阶段的性能,减少他的耗时。

所以当时在JVM参数模板中,同样加入了这两个参数:

-Xms4096M -Xmx4096M -Xmn3072M -Xss1M  -XX:PermSize=256M -XX:MaxPermSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSParallelInitialMarkEnabled -XX:+CMSScavengeBeforeRemark

调优后的效果

经过各个团队采用jstat观察JVM GC情况,发现明显有了很大的好转,基本上各个系统的Young GC都在几分钟一次,或者十几分钟一次,每次耗时就几十毫秒而已。

Full GC基本都在几天一次,每次耗时在几百毫秒的样子。

基本上各个系统的JVM达到这个性能,就对线上系统没多大影响了。哪怕是不太懂JVM优化的普通工程师只要套用这个模板,对一些普通的业务系统,都能保证其JVM性能不会出现大的问题,比如频繁的Young GC和Full GC导致的系统频繁卡顿。


思考

  • 你们公司有没有类似这里讲的JVM参数模板?
  • 假如你是公司的架构师,结合你们公司的大部分业务系统的实际情况,会如何定制一套JVM参数模板?
  • 是否你们公司有各种不同配置的机器?
  • 针对不同配置的机器如何定制JVM参数模板?
  • 你们公司有没有那种特例的系统,比如并发量特别高或者数据量非常大?
  • 对特例系统该如何进行优化?


相关文章
|
2天前
|
存储 监控 Java
JVM实战—7.如何模拟GC场景并阅读GC日志
本文主要介绍了:如何动手模拟出频繁Young GC的场景、JVM的Young GC日志应该怎么看、编写代码模拟动态年龄判定规则进入老年代、编写代码模拟S区放不下部分进入老年代、JVM的Full GC日志应该怎么看。
JVM实战—7.如何模拟GC场景并阅读GC日志
|
2天前
|
消息中间件 存储 算法
JVM实战—6.频繁YGC和频繁FGC的后果
本文详细探讨了JVM中的GC机制及其优化策略,涵盖Young GC、Old GC和Full GC的触发条件与影响。首先分析了JVM GC可能导致系统卡顿的问题,特别是大内存机器上的YGC性能瓶颈,并通过G1垃圾回收器解决。接着通过实际案例展示了频繁FGC的成因及优化方法,如调整新生代与老年代内存比例或使用大内存机器。最后总结了不同GC算法的适用场景及对象生命周期特点,为JVM性能调优提供了实用指导。
JVM实战—6.频繁YGC和频繁FGC的后果
|
5天前
|
缓存 算法 Java
JVM实战—4.JVM垃圾回收器的原理和调优
本文详细探讨了JVM垃圾回收机制,包括新生代ParNew和老年代CMS垃圾回收器的工作原理与优化方法。内容涵盖ParNew的多线程特性、默认线程数设置及适用场景,CMS的四个阶段(初始标记、并发标记、重新标记、并发清理)及其性能分析,以及如何通过合理分配内存区域、调整参数(如-XX:SurvivorRatio、-XX:MaxTenuringThreshold等)来优化垃圾回收。此外,还结合电商大促案例,分析了系统高峰期的内存使用模型,并总结了YGC和FGC的触发条件与优化策略。最后,针对常见问题进行了汇总解答,强调了基于系统运行模型进行JVM参数调优的重要性。
JVM实战—4.JVM垃圾回收器的原理和调优
|
7天前
|
消息中间件 Java 应用服务中间件
JVM实战—2.JVM内存设置与对象分配流转
本文详细介绍了JVM内存管理的相关知识,包括:JVM内存划分原理、对象分配与流转、线上系统JVM内存设置、JVM参数优化、问题汇总。
JVM实战—2.JVM内存设置与对象分配流转
|
1天前
|
存储 监控 Java
JVM实战—8.如何分析jstat统计来定位GC
本文详细介绍了使用jstat、jmap和jhat等工具分析JVM运行状况的方法,以及如何合理优化JVM性能。内容涵盖新生代与老年代对象增长速率、Young GC和Full GC的触发频率及耗时等关键指标的分析。通过模拟BI系统和计算系统的案例,展示了如何根据实际场景调整JVM参数以减少FGC频率,提升系统性能。最后汇总了常见问题及其解决方案,帮助开发者更好地理解和优化JVM运行状态。
JVM实战—8.如何分析jstat统计来定位GC
|
6天前
|
消息中间件 存储 算法
JVM实战—3.JVM垃圾回收的算法和全流程
本文详细介绍了JVM内存管理与垃圾回收机制,涵盖以下内容:对象何时被垃圾回收、垃圾回收算法及其优劣、新生代和老年代的垃圾回收算法、Stop the World问题分析、核心流程梳理。
JVM实战—3.JVM垃圾回收的算法和全流程
|
7天前
|
消息中间件 Java 应用服务中间件
JVM实战—1.Java代码的运行原理
本文介绍了Java代码的运行机制、JVM类加载机制、JVM内存区域及其作用、垃圾回收机制,并汇总了一些常见问题。
JVM实战—1.Java代码的运行原理
|
11天前
|
存储 JavaScript
(ERP系统查看DWG)MxCAD APP调用内部弹框的方法
MxCAD APP 二次开发提供了调用项目内部弹框的接口,以保持样式统一。用户需创建 `test_dialog` 文件夹并依次创建 `dialog.ts`、`dialog.vue` 和 `index.ts` 文件来注册、构建和渲染弹框。通过 `useDialogIsShow` 钩子函数控制弹框显示,并可在方法中直接调用 `dialog.showDialog()` 来控制弹框显隐。此外,还支持监听确认或取消事件获取数据,以及通过配置 `vite.config.ts` 解决样式冲突问题。最终在 `src/index.ts` 中引入相关文件即可实现弹框功能。
|
1天前
|
SQL 缓存 监控
JVM实战—9.线上FGC的几种案例
本文详细探讨了JVM性能优化中的几个关键案例与问题。首先分析了如何优化每秒十万QPS的社交APP,通过增加Survivor区大小和优化内存碎片解决频繁Full GC的问题。接着讨论了垂直电商后台系统FGC的深度优化,定制JVM参数模板以降低GC频率。还探讨了不合理设置JVM参数导致频繁FGC的情况,并提出了解决方案。此外,针对线上系统每天数十次FGC的问题,定位到大对象是主要原因,并通过调整新生代大小等参数优化。同时,分析了电商大促活动中因System.gc()调用导致系统卡死的现象,建议禁用显式GC。
JVM实战—9.线上FGC的几种案例
|
5天前
|
消息中间件 算法 Java
JVM实战—5.G1垃圾回收器的原理和调优
本文详细解析了G1垃圾回收器的工作原理及其优化方法。首先介绍了G1通过将堆内存划分为多个Region实现分代回收,有效减少停顿时间,并可通过参数设置控制GC停顿时长。接着分析了G1相较于传统GC的优势,如停顿时间可控、大对象不进入老年代等。还探讨了如何合理设置G1参数以优化性能,包括调整新生代与老年代比例、控制GC频率及避免Full GC。最后结合实际案例说明了G1在大内存场景和对延迟敏感业务中的应用价值,同时解答了关于内存碎片、Region划分对性能影响等问题。

热门文章

最新文章