记CMS FGC 的一次调优

2024-01-10 08:20
文章标签 一次 cms 调优 fgc

本文主要是介绍记CMS FGC 的一次调优,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

介绍

        有一个系统,有如下特征,偶尔会触发 FGC(1小时几次,每次持续4~5分钟):

  1. 机器规格 48C96G,规格已经很大了,不宜再扩大
  2. 内存分配:Young 20GB(1:1:8), Old  70GB, 堆外4GB, 预留 2GB 给 OS
  3. 使用 ParNew GC +  CMS GC
  4. 启动后需要加载大量元数据、缓存,大概占据 30GB~40GB 内存,这些元数据、缓存常驻内存
  5. 业务繁忙,堆内存分配速度很快,低峰期 1GB/s+,高峰期 5GB/s+
  6. 单机大部分时间不会FGC,Old区使用率也符合预期 (比例在30GB~40GB除以70GB);但即将发生FGC时,Old区的利用率是在短时间(2~3分钟以内)猛涨上去的,不是慢慢涨上去的
  7. 在不改代码的情况下,尝试过调整如下 GC 参数,效果不明显或者恶化
    1. 调大 Old、调小 Eden
    2. 使用 G1 GC
    3. 调整 CMS 开始的阈值(从68%改成45%)
  8. 每次执行完 FGC 后 Old 区使用率确实可以降下来,说明没有内存泄露

        我们希望能尽可能减少 FGC 发生频率,GC STW < 5 秒都能接受。

常见 FGC 原因

  1. 有内存泄露
  2. 申请超过容量的堆内存
  3. 碎片化严重 + 大对象
  4. Survivor 太小,YGC 后发生提前晋升,再加上 Old区的垃圾回收速度 < Old区的垃圾产生速度最终导致 FGC

        原因 1 属于代码问题,应该很好看出此类现象,GC 算法无能为力。

        原因 2 属于代码问题,瞬间申请的内存超过了总容量,GC 算法无能为力,可以考虑调低系统的并发度,否则最终系统会由于 OOM 挂掉。

        原因 3 属于 GC 算法问题,一个好的 GC 算法应该要能减轻此类问题,对此程序员不好做些什么改进。

        原因 4 是我们系统本次遇到的原因,下面我展开介绍。

GC提前晋升/过早晋升

        提前晋升是指如下流程的第3步:

  1. Java 执行到分配对象的代码
  2. Eden 区容量不足触发 YGC
  3. YGC 完成之后,Survivor 区容不下在本次 YGC 里存活下来的所有对象,此时部分对象就会提前晋升到 Old 区

提前晋升的危害

        Young 区 和 Old 区的回收算法不同,导致回收速度有很大差异,Old 区的垃圾回收速度是很慢的。如果你的系统里 Old 区的垃圾产生速度 > Old 区的垃圾回收速度,那么就很有可能出现 “concurrent mode failure” 引起 FGC。提前晋升会显著增加 “Old 区的垃圾产生速度”,因此更容易出现 FGC。

对象的声明周期长短

        一般来说我们认为:

  1. 短生命周期对象应该分配到 Young 区,它们一般在几次 YGC 内就必须结束生命。
  2. 长生命周期对象应该进入到 Old 区,这些对象一般是“长期”存在的,比如 static 级别的变量、整个 Spring 的 context 及其引用的 beans、配置过期时间为10分钟的cache等。

        在实际中偶尔会有中生命周期对象出现。

我自创的一些术语,大家能get到含义就行,至于叫法之后可以改改

        主观上它们其实是属于短生命周期对象,但由于各种原因它们撑过了好几次 YGC,最终由于年龄到了或者 Survivor 满了晋升到 Old 区。

        比如在我们的系统里需要经常向外发送 RPC 请求,请求体经 JSON 序列化之后大小几十MB~几百MB都有,为了构造这个请求体本身也需要很多中间对象,它们也是要占据内存的。如果 RPC 发生了超时了,那么这些中间对象不得不等到 RPC 回调执行才会被释放(因为在回调里还用到它们了)。

分析和优化

        我们认为我们系统发生 FGC 的直接原因是,某一时刻发爆发地执行大量 RPC 请求由于 RPC 需要等待较长时间才会报超时异常导致内存无法被及时回收,YGC 频率高(高峰时期大概3~4秒就需要一次 YGC),每个请求消耗的内存多,累加起来已经超过了 Survivor 区(当时我们的 Survivor 区大小才 2GB),导致不停有对象提前晋升到 Old 区,晋升速度超过 Old 区 GC 回收速度,引发 FGC。

        按这个思路,我们的优化是:

  1. 降低系统的并发度:这个优化在FGC之前就已经做了,我们限制最多有 16 个 RPC 请求同时在执行,不宜过低,过低可能就会出现明显的积压了(根据业务需求)
  2. 调大 Survivor 区,Survivor 默认是占 Young 区的 1/(1+1+8)=1/10=10%=2GB,将其调整到 1/(1+1+2)=25%=5GB 之后效果非常好,提前晋升到 Old 区的对象肉眼可见的变少了(可以通过看 Old 区使用量的监控曲线图);事实上我们可以适当将 Old 区的容量更多地划分到 Survivor 上效果会更好。
  3. 在执行 RPC 请求的过程中,将那些不会被引用的变量设置为null;在 callback 里如果要打印 request.size() 的需要将 request.size() 的值提前保存为一个 int 再在回调里引用,避免回调直接依赖 request 变量。

将变量设置为null有利于加速 GC 吗?

        先说结论:至少不会更差,在某些场景下能显著优化GC

        TODO 下面放一些验证代码

        

这篇关于记CMS FGC 的一次调优的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/590160

相关文章

Python判断for循环最后一次的6种方法

《Python判断for循环最后一次的6种方法》在Python中,通常我们不会直接判断for循环是否正在执行最后一次迭代,因为Python的for循环是基于可迭代对象的,它不知道也不关心迭代的内部状态... 目录1.使用enuhttp://www.chinasem.cnmerate()和len()来判断for

电脑多久清理一次灰尘合? 合理清理电脑上灰尘的科普文

《电脑多久清理一次灰尘合?合理清理电脑上灰尘的科普文》聊起电脑清理灰尘这个话题,我可有不少话要说,你知道吗,电脑就像个勤劳的工人,每天不停地为我们服务,但时间一长,它也会“出汗”——也就是积累灰尘,... 灰尘的堆积几乎是所有电脑用户面临的问题。无论你的房间有多干净,或者你的电脑是否安装了灰尘过滤器,灰尘都

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

JVM内存调优原则及几种JVM内存调优方法

JVM内存调优原则及几种JVM内存调优方法 1、堆大小设置。 2、回收器选择。   1、在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提供的内存查看工具,比如JConsole和Java VisualVM。   2、对JVM内存的系统级的调优主要的目的是减少

(function() {})();只执行一次

测试例子: var xx = (function() {     (function() { alert(9) })(); alert(10)     return "yyyy";  })(); 调用: alert(xx); 在调用的时候,你会发现只弹出"yyyy"信息,并不见弹出"10"的信息!这也就是说,这个匿名函数只在立即调用的时候执行一次,这时它已经赋予了给xx变量,也就是只是

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位 一、背景二、定位问题三、解决方法 一、背景 flume系列之:定位flume没有关闭某个时间点生成的tmp文件的原因,并制定解决方案在博主上面这篇文章的基础上,在机器内存、cpu资源、flume agent资源都足够的情况下,flume agent又出现了tmp文件无法关闭的情况 二、

jmeter之仅一次控制器

仅一次控制器作用: 不管线程组设置多少次循环,它下面的组件都只会执行一次 Tips:很多情况下需要登录才能访问其他接口,比如:商品列表、添加商品到购物车、购物车列表等,在多场景下,登录只需要1次,我们期望的是重复执行登陆后面的接口来做压测,这就和事务相关,例如 事务1: 登录—>添加购物车 事务2: 登录—>购物车列表 事务3: 登录—>商品列表—>添加购物车 … 一、仅一次控制器案例 在

一次生产环境大量CLOSE_WAIT导致服务无法访问的定位过程

1.症状 生产环境的一个服务突然无法访问,服务的交互过程如下所示: 所有的请求都是通过网关进入,之后分发到后端服务。 现在的情况是用户服务无法访问商旅服务,网关有大量java.net.SocketTimeoutException: Read timed out报错日志,商旅服务也不断有日志打印,大多是回调和定时任务日志,所以故障点在网关和商旅服务,大概率是商旅服务无法访问导致网关超时。 后

关于一次速度优化的往事

来自:hfghfghfg, 时间:2003-11-13 16:32, ID:2292221你最初的代码 Button1 34540毫秒 5638毫秒  Button2 我的代码 这个不是重点,重点是这个  来自:hfghfghfg, 时间:2003-11-13 16:54, ID:22923085528毫秒 不会吧,我是赛杨1.1G  128M内存  w2000, delphi6  128M

Linux系统性能调优详解

前言 在服务器运维和管理中,Linux系统的性能调优是确保服务稳定性和响应速度的关键。通过对系统进行细致的调优,可以显著提升处理能力,优化资源利用率。本文将详细介绍Linux性能调优的多个方面,包括系统监控、磁盘优化、内存管理、网络配置等,并提供实用的技巧和工具。 简介 Linux性能调优是一个涉及多个层面的复杂过程,旨在确保系统资源得到最佳利用,从而提高整体性能和响应速度。 调优实践