JVM调优—减少FullGC

背景

        最近负责了一个审批流程新项目,带领了几个小伙伴,哼哧哼哧的干了3个月左右,终于在三月底完美上线了,好消息是线上客户用的很丝滑,除了几个非常规的业务提单之外,几乎没有什么大的问题,但是美中不足的是,发现每个pod的GC频率非常高,基本上30分钟就会有一次FGC,导致每次流量高峰的时候,会有一部分客户反馈,系统有些卡顿,观察监控平台发现,每天流量高峰的时候FGC竟然达到了惊人的5分钟每次,每次GC的时间差不多有400-700ms,此时,部分接口的耗时达到了5s,因此接口优化和参数调优迫在眉睫;
        因为本项目是基础服务,每个业务方都会调用,所以当时申请节点内存大小的时候就富裕了一点,共部署了4个pod,每个pod资源是8核16G,但是观察监控平台发现,每个pod内存只使用不到2G,其中eden 200M old 500m survivor更是只有可怜的96m左右,导致年轻代很容易就占满了,存活的对象就被转移到老年代了,由于老年代分配的内存也特别少,QPS一高就会频繁的触发FullGC,导致系统卡顿甚至接口超时。
        排查代码发现有一个占比60%量一个接口虽然查询的表比较单一,但是查询了所有字段,其中一个字段存储的是一个JSON,但业务中却又没有使用到。

JVM常见参数

一、配置垃圾收集器

1、Serial垃圾收集器(新生代)

         开启:-XX:+UseSerialGC

         关闭:-XX:-UseSerialGC

         //新生代使用Serial  老年代则使用SerialOld

 ​

 2、ParNew垃圾收集器(新生代)

         开启 -XX:+UseParNewGC

         关闭 -XX:-UseParNewGC

         //新生代使用功能ParNew 老年代则使用功能CMS

 ​

 3、Parallel Scavenge收集器(新生代)

         开启 -XX:+UseParallelOldGC

         关闭 -XX:-UseParallelOldGC

         //新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器

 ​

 4、ParallelOld垃圾收集器(老年代)

        开启 -XX:+UseParallelGC

        关闭 -XX:-UseParallelGC

        //新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器

 ​

 5、CMS垃圾收集器(老年代)

        开启 -XX:+UseConcMarkSweepGC

        关闭 -XX:-UseConcMarkSweepGC

 ​

 6、G1垃圾收集器

         开启 -XX:+UseG1GC

         关闭 -XX:-UseG1GC

二、堆内存相关配置

设置堆初始值

         指令1:-Xms2g

 ​

 设置堆区最大值

         指令1:-Xmx2g

 ​

 新生代内存配置

         指令1:-Xmn512m

 ​

 2个survivor区和Eden区大小比率

         指令:-XX:SurvivorRatio=6  //S区和Eden区占新生代比率为1:6,两个S区2:6(默认是8,即8:1:1)

 ​

 新生代和老年代的占比

 -XX:NewRatio=4  //表示新生代:老年代 = 1:4 即老年代占整个堆的4/5;默认值=2

三、GC并行执行线程数

        -XX:ParallelGCThreads=16

四、进入老年代的GC年龄

         -XX:InitialTenuringThreshol=7 //年轻代对象转换为老年代对象最小年龄值,默认值7,对象在坚持过一次Minor GC之后,年龄就加1,每个对象在坚持过一次Minor GC之后,年龄就增加1

 ​

         -XX:MaxTenuringThreshold=15 //年轻代对象转换为老年代对象最大年龄值,默认值15

五、GC日志信息配置

        -Xloggc:/data/gclog/gc.log//固定路径名称生成

         -Xloggc:/home/GCEASY/gc-%t.log //根据时间生成

        打印GC的详细日志 

        开启 -XX:+PrintGCDetails

        关闭 -XX:-PrintGCDetails

六、在Full GC时生成dump文件

        -XX:+HeapDumpBeforeFullGC       //实现在Full GC前dump

        -XX:+HeapDumpAfterFullGC        //实现在Full GC后dump。

        -XX:HeapDumpPath=e:\dump        //设置Dump保存的路径


调优过程

    一、业务调优

       业务调优就不展开讲述了,主要是用到了arthas这个调优工具,trace了耗时比较久的接口,排除不优雅的编码之后,就来到了数据层面,由于项目使用的是postgres sql,并且已经分库+分区了,核心表的数据量级也是百万级别,所以最终关注的是索引,使用explain查看核心sql的执行计划,看其是否命中索引;

        补充一点,由于项目有一张历史表的数据量比较大,8000万左右,并且业务中也需要使用到,每个单据号对应的审批流程一般是流程节点的10倍左右,比如某个审批流程10个节点,那么改流程结束后就会产生100+条数据,在列表中使用到了改表中的某些数据,起初直接根据单据号进行查询,那么分页条数为1000的时候每次就会查询1000*100条记录,而业务真正需要关注的只是10个节点的审批结果而已,白白浪费90%的查询记录,因此,在业务中冗余了一个节点字段,标识是否是节点的审批结果,每次查询除了使用单据号之外,加上该字段,就大大的过滤了记录数量,这样做法有如下好处:

        ①减少网络传输量

        ②降低内存使用(起初是内存过滤,高cpu操作)

        ③防止OOM


二、JVM参数调优

        第一版

                第一版比较偷懒,直接上了G1,因此G1不用配置过多的参数,很多就自适应调节,但是一上线发现cpu就很容易飙升到80以上,并且接口耗时也慢了一倍,起初每个接口的耗时到该200ms,G1之后就变成400ms,终于在运行3天之后,就有监控告警有大量的接口请求超时,观察到jvm堆内存使用长时间100%,导致健康检查机制强行将节点重启了(5s检查一次,15未检查到就重启)为了不影响业务先增加了两个节点,降低QPS,从而降低堆内存使用。

                具体的参数配置

-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+PrintGC -Xloggc:/data/logs/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/ -XX:+UseG1GC -Xmx9G -Xms9G -XX:MaxDirectMemorySize=1g -XX:ConcGCThreads=8 -XX:MaxGCPauseMillis=500 

        第二版

        由于公司所有的项目都是K8s集群部署,所以JVM参数基本都用的是默认参数,这一次只是设置了Xms=11g,Xmx=10g,Xmn=4g,但是发现年轻代的from和to的比例不对,按照正常的默认8:1:1,应该是410m左右,但是监控上面显示的始终是121m,这样的话QPS达到高峰的时候,老年代上升的速率就比较快,3.76G,基本上12h就用完触发了FGC,这显然是不合理的,因为业务逻辑中没有需要常驻内存的对象,基本上朝生夕灭的,在年轻代就应该被回收,而导致出现这个原因是from和to的内存太小了,存货了15次之后的对象就被转移到老年代了。

        开始的时候使用-XX:-UseAdaptiveSizePolicy –XX:SurvivorRatio=8,确实改变年轻代的具体分配内存,但是使用 jmsp -heap 1命令发现,jvm的垃圾回收器是ParallelGC,并不是我们想要的CMS,因此不论年轻代from和to的设置了多大空间,其使用始终不会超过121m,老年代还是晋升的速率比较快,并没有彻底解决问题。具体参数配置如下

-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Xmx11g -Xms11g -Xmn5g -XX:PermSize=1g -XX:MaxPermSize=1g -XX:SurvivorRatio=8 -XX:-UseAdaptiveSizePolicy -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps

        第三版 最终版本

      开启CMS收集参数  -XX:+UseConcMarkSweepGC,监控和运行变得正常,堆空间各个区域的分配也是正常的。

-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseConcMarkSweepGC -Xmx11g -Xms11g -Xmn5g -XX:PermSize=1g -XX:MaxPermSize=1g -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:/data/logs/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs

        小结

        容器部署节点只分配1G,具体原因可以参考这篇文章频繁 GC 问题排查以及UseContainerSupport与MaxRAMPercentage的正确使用-CSDN博客

        按照理论上第二次调优就已经能够满足业务需求了,但是依据《深入理解Java虚拟机》讲的,jdk8默认的垃圾收集器是CMS,那么年轻代的eden、from和to分配内存空间的比例应该是8:1:1,显然目前我的数据不正确,进入到pod节点发现jvm使用的是ParallelGC,如下图所示


jvm调优常见问题

        常用命令

        jmap -heap pid 查看内存使用情况

推荐配置

        8C16G下的参数配置

综上所述,8C16G下,推荐使用如下的参数设置:

-Xmx12g -Xms12g

-XX:ParallelGCThreads=8

-XX:ConcGCThreads=2

-XX:+UseConcMarkSweepGC

-XX:+UseCMSInitiatingOccupancyOnly

-XX:CMSInitiatingOccupancyFraction=70

-XX:MaxGCPauseMillis=100  // 按业务情况来定

-XX:+PrintGCTimeStamps

-XX:+PrintGCDetails

-XX:+PrintGCDateStamps

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/611287.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mamba-ssm安装卡着不动

项目中用到Mamba的小伙伴,causal_conv1d和 mamba-ssm两个包,但是会卡在Building wheel for mamba-ssm (setup.py) : 为了探究卡在了building的哪一步,加入–verbose进行显示: pip install mamba-ssm --no-cache-dir -…

基于Springboot的微乐校园管理系统(有报告)。Javaee项目,springboot项目。

演示视频: 基于Springboot的微乐校园管理系统(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构…

C++ 抽象与封装

一 抽象 抽象实例:时钟 数据抽象: 具有表面当前时间的时、分、秒 行为抽象: 具有设置时间和显示时间两个最基本的功能。 抽象实例:人 数据抽象:姓名、年龄、性别等。 行为抽象: 生物属性:吃…

centos安装mysql-client

直接安装: yum install mysql-community-client报了错误No package mysql-community-client available. 原因:CentOS/RHEL系统默认的软件源中并不包含MySQL软件包,需要通过添加第三方存储库来获取MySQL相关软件 添加源 安装MySQL官方的Yum…

如何使用vue脚手架创建项目

前言 使用vue搭建项目的时候,我们可以通过对应的cmd命令去打开脚手架,然后自己配置对应的功能插件 说明: 要使用Vue脚手架创建项目,你需要先确保你已经安装了Node.js和npm(Node.js的包管理器)。然后&#…

【数据结构与算法】常见的排序算法

文章目录 排序的概念冒泡排序(Bubble Sort)插入排序(Insert Sort)选择排序(Select Sort)希尔排序(Shell Sort)写法一写法二 快速排序(Quick Sort)hoare版本&a…

kaldi学习参考

HMM模型 https://www.cnblogs.com/baixf-xyz/p/16777438.htmlhttps://www.cnblogs.com/baixf-xyz/p/16777438.htmlGMM-HMM 基于GMM-HMM的语音识别系统https://www.cnblogs.com/baixf-xyz/p/16777439.html https://www.cnblogs.com/baixf-xyz/p/16777426.htmlhttps://www.cnbl…

Java入门基础学习笔记13——数据类型

数据类型的分类: 基本数据类型 引用数据类型 基本数据类型:4大类8种类型: 定义整形用int,再大的数用long。 package cn.ensource.variable;public class VariableDemo2 {public static void main(String[] args) {//目标&#x…

Python学习笔记------json

json简介 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据 JSON本质上是一个带有特定格式的字符串 主要功能:json就是一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递和交互 为了让不同的语言能够相互通…

luceda ipkiss教程 69:导出器件或者线路的三维模型

ipkiss 3.12版加入write_obj函数,可以直接输出器件的三维模型。 如,输出自定义的mmi的三维模型: 代码如下: from si_fab import all as pdk from ipkiss3 import all as i3class MMI1x2(i3.PCell):"""MMI with …

【C++】学习笔记——优先级队列

文章目录 十、优先级队列1. priority_queue的介绍2. 优先级队列如何使小的数据优先级高3. 仿函数介绍4. priority_queue的模拟实现 补: 反向迭代器未完待续 十、优先级队列 1. priority_queue的介绍 优先级队列 其实也不属于队列,它跟 stack 和 queue …

【MQTT】mosquitto 的 “下载、交叉编译、使用” 详细教程,手把手搭建一个MQTT Broker

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…

ARIMA模型在河流水质预测中的应用_含代码

#水质模型 #时间序列 #python应用 ARIMA 时间序列模型简介 时间序列是研究数据随时间变化而变化的一种算法,是一种预测性分析算法。它的基本出发点就是事物发展都有连续性,按照它本身固有的规律进行。ARIMA(p,d,q)模型全称为差分自回归移动平均模型 (A…

单链表经典oj题(2)

前言 这次将要把剩下的oj题将以图解和自己的理解把它讲解完,希望对大家有所帮助,这次的讲解也是干货 第一题 21. 合并两个有序链表 - 力扣(LeetCode) ok这次就简单点,大家自己去看题目了 将两个升序链表合并为一个…

【Linux】进程的七大状态详解!

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …

如何查看centos7中Java在哪些路径下

在 CentOS 7 上,你可以通过几种方式查找安装的 Java 版本及其路径。以下是一些常用的方法: 1. 使用 alternatives 命令 CentOS 使用 alternatives 系统来管理同一命令的多个版本。你可以使用以下命令来查看系统上所有 Java 安装的配置: su…

C++动态内存管理:与C语言动态内存管理的差异之争

当你改错一行代码的时候: 当你想要重构别人的代码时: 目录 前言 一、C/C的内存分布 二、C/C语言中的动态内存管理 三、new与delete的实现原理 总结: 前言 在C中,内存管理是一个至关重要的主题。正确地管理内存可以避免内存泄…

上海AI Lab开源首个可替代GPT-4V的多模态大模型

与开源和闭源模型相比,InternVL 1.5 在 OCR、多模态、数学和多轮对话等 18 个基准测试中的 8 个中取得了最先进的结果。 上海AI Lab 推出的 InternVL 1.5 是一款开源的多模态大语言模型 (MLLM),旨在弥合开源模型和专有商业模型在多模态理解方面的能力差距…

二、SPI协议

文章目录 总述1.SPI接口2. SPI工作模式3. SPI通信时序4. SPI协议 对比 UART协议(上一篇文章刚介绍过uart协议,这里来对比一下) 总述 SPI(Serial Peripheral Interface)是一种高速的、全双工、同步的串行通信总线&…
最新文章