如何优化生产环境的Full GC?_优化生产工艺流程,提高生产效率

createh53周前 (02-25)技术教程10

大部分工程师开发完一个系统后,部署生产环境的时候往往不对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达到这个性能就对线上系统没多大影响。

相关文章

JAVA系列-GC_java &gt

1. OOM1) 常见OOM异常类型(1) StackOverFlowError栈内存溢出,用于深度方法调用(循环递归);(2) OutOfMemoryError:Java heap space用于变...

大佬带你深入解析java虚拟机:垃圾优先的垃圾回收器(G1 GC)

G1 GCG1 GC是面向服务端应用程序的垃圾回收器,通过新的堆设计和停顿预测模型,可以到达用户指定的一个比较合理的软实时目标。本章将详细分析G1 GC的设计和实现。G1 GC简介 基于Region的...

漫谈:Java GC的那些事(一)_java gc1

前言与C语言不同,Java内存(堆内存)的分配与回收由JVM垃圾收集器自动完成,这个特性深受大家欢迎,能够帮助程序员更好的编写代码,本文以HotSpot虚拟机为例,说一说Java GC的那些事。Jav...

JVM成神路之性能调优篇:GC调优、Arthas工具详解及线上最佳配置

引言“在当前的互联网开发模式下,系统访问量日涨、并发暴增、线上瓶颈等各种性能问题纷涌而至,性能优化成为了现时代开发过程中炙手可热的名词,无论是在开发、面试过程中,性能优化都是一个常谈常新的话题”。Ja...

Java中9种常见的CMS GC问题分析与解决(一)

目前,互联网上 Java 的 GC 资料要么是主要讲解理论,要么就是针对单一场景的 GC 问题进行了剖析,对整个体系总结的资料少之又少。前车之鉴,后事之师,美团的几位工程师历时一年多的时间,搜集了内部...

关于Java垃圾回收,你必须要知道FullGC是什么

本文共3198字,是本人前几天面试被提问到的一个问题,将在该文中阐述关于Java垃圾回收——Full GC的相关知识,包括定义、触发条件、具体过程。前几天面试的时候,面试官在最后问了我一个有关Full...