网站首页 > 技术文章 正文
大部分工程师开发完一个系统后,部署生产环境的时候往往不对JVM进行参数设置,直接用默认JVM参数,这绝对是系统负载逐渐增高的时最大问题
如你不设置-Xmx、-Xms之类的堆内存大小,你启动一个系统,可能默认就给你几百MB的堆内存大小,新生代和老年代可能都是几百M。
很多后台系统都用默认JVM参数部署启动,前期没啥问题,但中后期开始,当有一定用户量和一定负载,就会出现惊喜。
Eden过小,导致频繁触发YGC,Survivor过小,导致经常在YGC后存活对象其实也没多少,但Survivor放不下,导致对象经常进入老年代,导致老年代过段时间就满,然后触发Full GC。
所以当时这个垂直电商APP的各个系统通过jstat分析JVM GC后发现,高峰期Full GC每小时发生好几次。Full GC正常以天为单位发生,如每天发生一次或几天发生一次。要是每h都发生几次Full GC,就会导致系统每h卡顿好几次!
公司级别JVM参数模板
让大部分系统套用这个模板,基本保证JVM性能别太差,避免很多初中级工程师直接使用默认的JVM参数,可能一台8G内存的机器上,JVM堆内存就分配了几百MB。
-Xms4096M
-Xmx4096M
-Xmn3072M
-Xss1M
-XX:PermSize=256M
-XX:MaxPermSize=256M
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFaction=92
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0
8G机器给JVM堆分配4G,毕竟还有其他进程使用内存,别让JVM堆把机器内存占满。
年轻代给到3G,让年轻代尽量大,进而让每个Survivor区域都达到300MB。根据当时对这个业务系统的分析,假设用默认的JVM参数,可能年轻代就几百MB的内存,Survivor区域就几十M。
那每次GC后,存活对象可能几十M,因为在GC瞬间可能有部分请求没处理完,此时会有几十M对象存活,所以很容易触发动态年龄判定规则,让部分对象进入老年代。
所以分析后,给年轻代更大内存空间,让Survivor更大,这样在YGC时,这瞬间可能有部分请求没处理完,有几十M存活对象,这时候在几百M的Survivor可轻松放下,而不会进老年代。这样操作对垂直电商大部分后台业务都能cover。
不同系统运行时情况略不同,但基本上都是在每次YGC后存活几~几十M对象,所以此时在这个参数模板下都能抗住。
只要把内存分配完毕,那对象进入老年代速度就很慢,经过该参数模板在朋友公司全部系统的重新部署和上线,各个团队通过jstat观察,基本上发现各个系统的Full GC都变成了几天才会发生一次。
此时在参数模板里还会加入Compaction相关参数,保证每次Full GC后都会执行一次压缩,解决内存碎片。
如何优化每次Full GC的性能?
就是把每次Full GC时间进一步降低。
- -XX:+CMSParallelInitialMarkEnabled,会在CMS的“初始标记”阶段开启多线程并发执行初始标记阶段,会STW,该阶段开启多线程并发后,可尽可能优化该阶段性能,减少STW时间。
- -XX:+CMSScavengeBeforeRemark在CMS重新标记阶段前,先尽量执行一次YGC
这样做有什么作用呢?
CMS重新标记也会STW,所以重新标记前,先执行一次YGC,就会回收掉一些年轻代里无人引用的对象。
所以若提前回收掉一些对象,在CMS重新标记阶段就能少扫描一些对象,这就提升CMS重新标记阶段的性能。
-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
采用JVM参数模板后的效果
采用jstat观察JVM GC情况,发现好转,各系统:
- YGC都在几min或十几min一次,每次耗时就几十ms
- Full GC基本都在几天一次,每次耗时在几百ms
JVM达到这个性能就对线上系统没多大影响。
猜你喜欢
- 2024-10-28 美团面试:熟悉哪些JVM调优参数,幸好我准备过
- 2024-10-28 Java虚拟机:Jvm概念和原理详解以及GC机制的分析
- 2024-10-28 JVM 的内存模型(jvm1.8内存模型)
- 2024-10-28 JDK、JRE、JVM,是什么关系?(jdk jrejvm的区别)
- 2024-10-28 一个 JVM 参数引发的频繁 CMS GC(当产生一个异常时,jvm会做什么)
- 2024-10-28 JVM系列一:JVM内存组成及分配(jvm的内存分配)
- 2024-10-28 谈谈JMM与JVM的相关知识(jmm jvm juc)
- 2024-10-28 常见的JVM参数配置(常见的JVM参数配置有哪些)
- 2024-10-28 一份详细介绍JVM的资料(对比JDK8和JDK7)
- 2024-10-28 理解JVM运行时数据区域,看这一篇文章就够了
- 最近发表
-
- count(*)、count1(1)、count(主键)、count(字段) 哪个更快?
- 深入探索 Spring Boot3 中 MyBatis 的 association 标签用法
- js异步操作 Promise fetch API 带来的网络请求变革—仙盟创梦IDE
- HTTP状态码超详细说明_http 状态码有哪些
- 聊聊跨域的原理与解决方法_跨域解决方案及原理
- 告别懵圈!产品新人的接口文档轻松入门指南
- 在Javaweb中实现发送简单邮件_java web发布
- 优化必备基础:Oracle中常见的三种表连接方式
- Oracle常用工具使用 - AWR_oracle工具有哪些
- 搭载USB 3.1接口:msi 微星 发布 990FXA Gaming 游戏主板
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)