当前位置: 首页 > news >正文

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

相关文章:

JVM调优—减少FullGC

背景 最近负责了一个审批流程新项目,带领了几个小伙伴,哼哧哼哧的干了3个月左右,终于在三月底完美上线了,好消息是线上客户用的很丝滑,除了几个非常规的业务提单之外,几乎没有什么大的问题,但是…...

力扣 256. 粉刷房子 LCR 091. 粉刷房子 python AC

动态规划 class Solution:def minCost(self, costs):row, col len(costs), 3dp [[0] * col for _ in range(row 1)]for i in range(1, row 1):for j in range(col):dp[i][j] costs[i - 1][j - 1]if j 0:dp[i][j] min(dp[i - 1][1], dp[i - 1][2])elif j 1:dp[i][j] m…...

C++STL细节,底层实现,面试题04

文章目录 19. STL19.1. 序列容器19.1.1. vector19.1.1.1. 底层实现和特点19.1.1.2. 常用函数19.1.1.3. emplace_back() vs push_back() 19.1.2. array19.1.2.1. 底层实现和特点19.1.2.2. 常用函数 19.1.3. deque19.1.3.1. 底层实现和特点19.1.3.2. 常用函数 19.1.4 list19.1.4.…...

Linux查看Oracle数据库的环境变量

Linux查看Oracle数据库的环境变量 在Linux上查看Oracle数据库的环境变量,通常涉及检查当前shell会话中已设置的环境变量。这些环境变量可能包括ORACLE_HOME、ORACLE_SID、PATH(可能包含Oracle二进制文件的路径)等。 以下是几种方法来查看这…...

pg数据库学习知识要点分析-1

知识要点1 对象标识OID 在PostgreSQL内部,所有的数据库对象都通过相应的对象标识符(object identifier,oid)进行管理,这些标识符是无符号的4字节整型。数据库对象与相应oid 之间的关系存储在对应的系统目录中&#xf…...

【Web】CTFSHOW 七夕杯 题解

目录 web签到 easy_calc easy_cmd web签到 CTF中字符长度限制下的命令执行 rce(7字符5字符4字符)汇总_ctf中字符长度限制下的命令执行 5个字符-CSDN博客7长度限制直接梭了 也可以打临时文件RCE import requestsurl "http://4ae13f1e-8e42-4afa-a6a6-1076acd08211.c…...

react native 设置屏幕锁定

原生配置 android 在android/app/src/main/AndroidManifest.xml在这个文件里的入口activity里添加 android:screenOrientation"portrait" <activityandroid:name".MainActivity"android:label"string/app_name" …...

探索 IPv6 协议:互联网的新一代寻址

目录 一.概述 IPv4 的问题和 IPv6 的新特性 IPv6 协议体系 二.IPv6 寻址架构&#xff1a;巨大的地址空间与灵活的寻址模式 IPv6 寻址概述 地址表示方法 地址前缀与地址类型标识 单播地址 任播地址 多播地址 特殊的 IPv6 地址 IPv6 主机与路由器寻址 地址分配 三.I…...

Ubuntu意外断电vmdk损坏--打不开磁盘“***.vmdk”或它所依赖的某个快照磁盘。

背景&#xff1a;电脑资源管理器崩溃卡死&#xff0c;强行断电重启&#xff0c;结果虚拟机打不开了&#xff0c;提示打不开磁盘“***.vmdk”或它所依赖的某个快照磁盘。 删除lck文件&#xff1a;失败vmware-vdiskmanager修复 &#xff1a;提示无法修复最终用 VMFS Recovery挂载…...

202466读书笔记|《一天一首古诗词》——借问梅花何处落,风吹一夜满关山

202466读书笔记|《一天一首古诗词》——借问梅花何处落&#xff0c;风吹一夜满关山 上册下册 《一天一首古诗词》作者李锡琴&#xff0c;蛮早前加入书架的已购买书籍&#xff0c;这次刚好有点时间&#xff0c;利用起来读完。 赏析没有细看&#xff0c;只读了诗词部分&#xff0…...

如何调用本地ollama的http请求接口

http://127.0.0.1:11434/api/generate 使用http post请求&#xff0c;参数 { "model": "qwen", "prompt": "为什么天空是蓝色?", "stream": false } 返回结果如下&#xff1a; {"model": "qwen",…...

【C】190 颠倒二进制位

颠倒给定的 32 位无符号整数的二进制位。 提示&#xff1a; 请注意&#xff0c;在某些语言&#xff08;如 Java&#xff09;中&#xff0c;没有无符号整数类型。在这种情况下&#xff0c;输入和输出都将被指定为有符号整数类型&#xff0c;并且不应影响您的实现&#xff0c;因…...

蓝桥杯备战5.图书管理员

[NOIP2017]图书管理员 (nowcoder.com) #include<bits/stdc.h> #define endl \n #define int long long using namespace std; const int N 2e510,M1e310; int a[N]; int n,q; int check(int l,int x) {int tmppow(10,l);for(int i1;i<n;i){if(a[i]%tmpx){cout<&…...

微型显示器可以实时监测大脑活动

美国团队开发基于LED的设备&#xff0c;以可视化大脑活动&#xff0c;在脑外科手术中指导神经外科医生 来自加州大学圣地亚哥分校和马萨诸塞州总医院的工程师和医生开发了一种薄膜显示设备&#xff0c;该设备结合了电极网格和特殊的GaN LED&#xff0c;可以在手术过程中实时跟…...

移动端适配方案

移动端适配 方案 1&#xff1a;rem html font-size 方案 2&#xff1a;vw rem html font-size rem 是相对于 html 元素的 font-size 来设置的单位&#xff0c;通过在不同屏幕尺寸下动态修改 html 元素的 font-size 可达到适配效果 在开发中&#xff0c;我们只需要考虑两个…...

【Ajax零基础教程】-----第一课 Ajax简介

一、什么是ajax ajax即 Asynchronous javascript And XML (异步 javaScript 和 XML) 是一种创建交互式&#xff0c;快速动态应用的网页开发技术&#xff0c;无需重新加载整个网页的情况下&#xff0c;能够更新页面局部数据的技术。 二、为什么使用Ajax 通过在后台与服务器进行少…...

大型医疗挂号微服务“马上好医”医疗项目(5)Swagger的使用

Swagger的简单介绍 Swagger 是一个 RESTful 接口文档的规范和工具集&#xff0c;它的目标是统一 RESTful 接口文档的格式和规范。在开发过程中&#xff0c;接口文档是非常重要的一环&#xff0c;它不仅方便开发者查看和理解接口的功能和参数&#xff0c;还能帮助前后端开发协同…...

C语言从头学04——介绍占位符和输出格式

占位符、输出格式都是与 printf() 相关的&#xff0c;当然其它函数也有用到占位符的。这里先介绍它们在 printf() 的使用。 一、先介绍占位符&#xff0c;所谓“占位符”通俗讲就是先占个位置&#xff0c;后边再找具体值(参数)代入进行显示的一种方法。先用一个例子说明…...

写爬虫代码抓取Asterank中小行星数据

2024年5月4日 问题来源 解决方案 回顾2023年7月14日自己写的爬虫代码 import requests import re import pandas as pd texts[] def getData(page):#每页评论的网址urlhttps://item.jd.com/51963318622.html#comment#添加headers&#xff0c;伪装成浏览器headers{User-Agent:…...

leetCode81. 搜索旋转排序数组 II

leetCode81. 搜索旋转排序数组 II 题目思路 可以二分后的具体思路见我的上篇博客 搜索旋转排序数组 代码 class Solution { public:bool search(vector<int>& nums, int target) {if(nums.empty()) return false;int R nums.size() - 1;while(R > 0 &&…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...