news 2026/6/30 10:07:29

日常问题排查-Younggc突然变长

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
日常问题排查-Younggc突然变长

前言

研发突然反馈一个版本上线后线上系统younggc时间变长,而这个版本修改的代码就是非常普通的CRUD,但是younggc时间就硬生生暴涨了100%。导致天天告警,虽然问题不大,但非常想知道原因,于是向我求助。

问题现场

如下图所示,younggc一个版本后从60ms涨到了120ms。而告警的阈值在100ms,导致每次younggc都告警。显然,调整告警阈值是最简单的方案,但作为一个有好奇心对技术很执着的同学,他很纠结于这个问题。当然我也是遇到有意思的问题就必须去一探究竟的性格,两者一拍即合。

younggc_slower

代码改动

第一反应肯定是去看代码的变化,代码改变如下所示:

改之前:

void functionA(){

......

a.setBuinsessKey(key1);

......

}

改之后:

void functionB(){

......

a.setBusinessKey(Key1);

a.setBuinessesKey(Key2);

a.setBusinessKey(Key3);

......

}

看着这微小的改动引起了这么大的变化,这是不可能的。笔者第一时间便否决了这种可能性。

流量变化

以这么多年我troubleshooting的第一直觉,肯定是负载变化才能引起younggc耗时的变化。询问那位同学,他明显是已经考虑了这样的问题。当即甩给了我相关的监控截图。从分钟级的尺度来讲,没有任何变化。

younggc_slower

容器问题

由于younggc耗时高集中在几台机器上,于是笔者登陆上那几台机器,发现在younggc耗时变高的时候都出现了nr_throttled,也就是当前容器的CPU时间片消耗达到了配置的cgroup上限,导致被kernel强制offline了。但到底是younggc导致了throttle还是throttle导致了younggc,这个因果关系以现有的信息来说完全无法判断。

nr_throttled_delta

Younggc日志

既然不能一眼看出来原因,那只能上去分析gc日志了,比对一下gc。发现差距在Object Copy上。

偏长younggc 120ms

[Object Copy (ms): Min: 107.5, Avg:108.2,Max:108.9,Diff:0.8,Sum: 869.5]

正常younggc 60ms

[Object Copy (ms): Min: 55.2, Avg: 56.1, Max:56.3, Diff: 1.1,Sum: 450.0]

嗯,从这个数据来判断,肯定是要Copy的数据变多了。为什么会变多呢?看看其younggc后的各年代的变化。

偏长younggc 120ms

[Eden: 4804.0M(4804.0M)->0.0B(4784.0M) Survivors: 108.0M->128.0M Heap: 6632.7M(8192.0M)->1848.8M(8192.0M)]

正常younggc 60ms

[Eden: 4848.0M(4848.0M)->0.0B(4832.0M) Survivors: 64.0M->80.0M Heap: 6717.7M(8192.0M)->1888.0M(8192.0M)]

笔者敏锐的观察到Survivor区变大了。偏长Younggc Survivor区是108M,而正常Younggc Survivor区是64M。

Object Copy哪些数据

问了一下gemini,object copy的对象一般有从

1.从Eden Region复制到Survivor Region

2.从Survivor Region复制到老年代Region

这两个地方进行Copy。也就是说,如果Survivor区变大了之后,Object Copy的数据应该是会变多。

嗯,虽然大模型经常忽悠我,但这个回答还是比较符合逻辑的,姑且就相信他。

观察gc监控

鉴于上面的分析,笔者突然觉得Younggc的时间很有可能和Survivor区的大小成正比关系。一旦Survivor区大于一个阈值,那么Younggc时间就会超过100ms,进而导致告警。为了验证这个关系,笔者去观察了不同机器正常younggc和偏长younggc的关系。发现完全符合这个规律。

为什么Survivor区会变化

由于分钟级的请求量没有任何变化。监控当前的精度只能到分钟级,于是笔者只能在请求机器上的日志中寻找相关的线索。比较了相关的业务日志,笔者敏锐的发现了一个不一样的地方。在相同数据量的情况下,分批的次数越多它的Younggc越长。

如果一共需要处理100W条数据

case 1: 分了10000批,每批处理1000条数据,Survivor区100M。对应的younggc在100-120ms

case 2: 分了5000批,每批处理2000条数据,Survivor区50M,对应的youggc在50-70ms

发现了这个规律之后,笔者去看了下业务系统的源代码。其伪代码如下所示:

// 这边的batchNo就是

for(long batchNo : batchNoList) {

// 按照批次从数据库中捞取相关数据

List<Task> task = selectFromDBBatchNo(bachNo);

// 处理相关数据

handleTask(task);

}

看到这段代码,上面的规律就很好理解了,batchNoList以及其相关数据是一直在for的计数里面,一直到处理完毕整个batchNoList以及其相关数据的引用都不会被回收。所以batchNoList(批次)数量越大,在Survivor区younggc的时候需要copy到old区的数据量就越多。反而在每次循环的循环体中,通过batchNo从数据库中捞取的数据在处理完之后就没用了,younggc的时候自然消亡,完全不参与相关younggc运算。

long_vs_normal

为什么以前没问题上了个版本就出问题了

首先这个肯定和修改的代码没有关系,笔者推测是由于上游流量的变化导致了批次数量的变化。这个也在后续的数据分析中得到了相关的印证。

解决方案

找到根本原因之后,解决这个问题就很简单了,将分批数给设定一个最大值,强制限制分批大小。代码只需寥寥数行,但是要知道怎么改则需要大量的观察和分析。

总结

Younggc时间变长是一个比较棘手的问题,因为它的可观测信息过少,而且往往和代码变更无关,而和请求分布导致的内存生成速率与存活分布有关。这时候只有通过大量的观察来分析各种数据间的相关性然后通过底层原理推断出因果性,最后才能找到根因。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/29 3:57:10

智能简历解析终极指南:如何用AI技术精准提取关键信息

智能简历解析终极指南&#xff1a;如何用AI技术精准提取关键信息 【免费下载链接】Resume-Matcher Resume Matcher is an open source, free tool to improve your resume. It works by using language models to compare and rank resumes with job descriptions. 项目地址…

作者头像 李华
网站建设 2026/6/28 1:52:52

springAI学习 一

一、Spring AI 概述 什么是Spring AI&#xff1f; Spring生态的AI集成框架 统一API访问不同AI服务&#xff08;OpenAI、Azure OpenAI、Anthropic等&#xff09; 支持多种AI功能&#xff1a;聊天、文生图、嵌入、向量存储等 Spring AI 是一个用于 AI 工程的应用框架。 其目标…

作者头像 李华
网站建设 2026/6/30 4:20:42

串口助手唐老鸭版:解决你串口调试痛点的终极方案

串口助手唐老鸭版&#xff1a;解决你串口调试痛点的终极方案 【免费下载链接】串口助手唐老鸭版使用说明 串口助手(唐老鸭版)是一款功能强大且易于使用的串口调试工具&#xff0c;专为开发者设计。其界面友好&#xff0c;操作简单&#xff0c;能够满足各种串口调试需求。无论是…

作者头像 李华
网站建设 2026/6/29 23:01:34

30秒创建一个智能解压工具:快马平台体验

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个简单的图形界面解压工具原型&#xff0c;功能包括&#xff1a;1)文件选择对话框 2)解压目标路径选择 3)显示压缩包内容预览 4)进度条显示 5)解压完成通知。使用Pythontkint…

作者头像 李华
网站建设 2026/6/29 20:59:49

每日一题Day08-数组的第K大元素

题面首先看我第一眼看到这道题的解法代码class Solution {public int findKthLargest(int[] nums, int k) {int n nums.length;Arrays.sort(nums);return nums[n - k];} }这样解好像也可以&#xff0c;但好像又在耍流氓&#xff0c;所以我就去看题解了最后看到一道一下用自己的…

作者头像 李华
网站建设 2026/6/30 3:26:58

基于VUE的网上预约挂号系统[VUE]-计算机毕业设计源码+LW文档

摘要&#xff1a;随着医疗信息化的发展&#xff0c;网上预约挂号系统在优化医疗服务流程、提高患者就医体验方面发挥着重要作用。本文设计并实现了一个基于VUE的网上预约挂号系统&#xff0c;该系统具备系统用户管理、新闻数据管理、系统简介设置、变幻图设置、用户管理、医生管…

作者头像 李华