一次线上JVM GC 长暂停排查,加班搞了好久

给大家分享一篇我在知乎上看到的,针对长时间 GC 问题排查定位过程的文章。

最终原因定位到 swap 空间上,是我未曾设想过的角度,因为常规的 GC 问题,相当大一部分原因最终定位出来都是代码相关、流量相关、配置相关的,所以这个是让我耳目一新的。

也算是提供了一种问题排查的新方向和新思路,我是有所收获的,也分享给你,希望你也能有所收获。

作者:京东科技 徐传乐

原文链接:https://zhuanlan.zhihu.com/p/597891369

背景

在高并发下,Java程序的GC问题属于很典型的一类问题,带来的影响往往会被进一步放大。不管是「GC频率过快」还是「GC耗时太长」,由于GC期间都存在Stop The World问题,因此很容易导致服务超时,引发性能问题。

事情最初是线上某应用垃圾收集出现Full GC异常的现象,应用中个别实例Full GC时间特别长,持续时间约为15~30秒,平均每2周左右触发一次;

图片

图片

JVM参数配置:

-Xms2048M –Xmx2048M –Xmn1024M –XX:MaxPermSize=512M

图片

排查过程

分析 GC 日志

GC 日志它记录了每一次的 GC 的执行时间和执行结果,通过分析 GC 日志可以调优堆设置和 GC 设置,或者改进应用程序的对象分配模式。

这里Full GC的reason是Ergonomics,是因为开启了UseAdaptiveSizePolicy,jvm自己进行自适应调整引发的Full GC。

这份日志主要体现GC前后的变化,目前为止看不出个所以然来。

图片

开启GC日志,需要添加如下 JVM 启动参数:

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/export/log/risk_pillar/gc.log

常见的 Young GC、Full GC 日志含义如下:

图片

进一步查看服务器性能指标

获取到了GC耗时的时间后,通过监控平台获取到各个监控项,开始排查这个时点有异常的指标,最终分析发现,在5.06分左右(GC的时点),CPU占用显著提升,而SWAP出现了释放资源、memory资源增长出现拐点的情况(详见下图红色框,橙色框中的变化是因修改配置导致,后面会介绍,暂且可忽略)

图片

JVM用到了swap?

是因为GC导致的CPU突然飙升,并且释放了swap交换区这部分内存到memory?

为了验证JVM是否用到swap,我们通过检查proc下的进程内存资源占用情况

for i in (cd/proc;ls∣grep"[0−9]"∣awk′0 >100');
do awk '/Swap:/{a=a+2}END{print '"i"',a/1024"M"}' /proc/$i/smaps 2>/dev/null;
done | sort -k2nr | head -10 # head -10 表示 取出 前10个内存占用高的进程 
# 取出的第一列为进程的id 第二列进程占用swap大小

看到确实有用到305MB的swap

图片

这里简单介绍下什么是swap?

swap指的是一个交换分区或文件,主要是在内存使用存在压力时,触发内存回收,这时可能会将部分内存的数据交换到swap空间,以便让系统不会因为内存不够用而导致oom或者更致命的情况出现。

当某进程向OS请求内存发现不足时,OS会把内存中暂时不用的数据交换出去,放在swap分区中,这个过程称为swap out。

当某进程又需要这些数据且OS发现还有空闲物理内存时,又会把swap分区中的数据交换回物理内存中,这个过程称为swap in。

为了验证GC耗时与swap操作有必然关系,我抽查了十几台机器,重点关注耗时长的GC日志,通过时间点确认到GC耗时的时间点与swap操作的时间点确实是一致的。

进一步查看虚拟机各实例 swappiness 参数,一个普遍现象是,凡是发生较长Full GC的实例都配置了参数 vm.swappiness = 30(值越大表示越倾向于使用swap);而GC时间相对正常的实例配置参数 vm.swappiness = 0(最大限度地降低使用swap)。

swappiness 可以设置为 0 到 100 之间的值,它是Linux的一个内核参数,控制系统在进 行swap时,内存使用的相对权重。

  • swappiness=0: 表示最大限度使用物理内存,然后才是 swap空间

  • swappiness=100: 表示积极的使用swap分区,并且把内存上的数据及时的交换到swap空间里面

图片

图片

对应的物理内存使用率和swap使用情况如下

图片

图片

至此,矛头似乎都指向了swap。

问题分析

当内存使用率达到水位线(vm.swappiness)时,linux会把一部分暂时不使用的内存数据放到磁盘swap去,以便腾出更多可用内存空间;

当需要使用位于swap区的数据时,再将其换回内存中,当JVM进行GC时,需要对相应堆分区的已用内存进行遍历;

假如GC的时候,有堆的一部分内容被交换到swap空间中,遍历到这部分的时候就需要将其交换回内存,由于需要访问磁盘,所以相比物理内存,它的速度肯定慢的令人发指,GC停顿的时间一定会非常非常恐怖;

进而导致Linux对swap分区的回收滞后(内存到磁盘换入换出操作十分占用CPU与系统IO),在高并发/QPS服务中,这种滞后带来的结果是致命的(STW)。

问题解决

至此,答案似乎很清晰,我们只需尝试把swap关闭或释放掉,看看能否解决问题?

如何释放swap?

设置vm.swappiness=0(重启应用释放swap后生效),表示尽可能不使用交换内存

方案 a:临时设置方案,重启后不生效

  1. 设置vm.swappiness为0,sysctl vm.swappiness=0

  2. 查看swappiness值,cat /proc/sys/vm/swappiness

方案b:永久设置方案,重启后仍然生效

  1. vi /etc/sysctl.conf

  2. 关闭交换分区swapoff –a(前提:首先要保证内存剩余要大于等于swap使用量,否则会报Cannot allocate memory!swap分区一旦释放,所有存放在swap分区的文件都会转存到物理内存上,可能会引发系统IO或者其他问题。)

查看当前swap分区挂载在哪:

图片

关停分区:

图片

关闭swap交换区后的内存变化见下图橙色框,此时swap分区的文件都转存到了物理内存上

图片

关闭Swap交换区后,于2.23再次发生Full GC,耗时190ms,问题得到解决。

图片

疑惑

  1. 是不是只要开启了swap交换区的JVM,在GC的时候都会耗时较长呢?

  2. 既然JVM对swap如此不待见,为何JVM不明令禁止使用呢?

  3. swap工作机制是怎样的?这台物理内存为8g的server,使用了交换区内存(swap),说明物理内存不够使用了,但是通过free命令查看内存使用情况,实际物理内存似乎并没有占用那么多,反而Swap已占近1G?

图片

free:除了buff/cache剩余了多少内存

shared:共享内存

buff/cache:缓冲、缓存区内存数(使用过高通常是程序频繁存取文件)

available:真实剩余的可用内存数

进一步思考

大家可以想想,关闭交换磁盘缓存意味着什么?

其实大可不必如此激进,要知道这个世界永远不是非0即1的,大家都会或多或少选择走在中间,不过有些偏向0,有些偏向1而已。

很显然,在swap这个问题上,JVM可以选择偏向尽量少用,从而降低swap影响,要降低swap影响有必要弄清楚Linux内存回收是怎么工作的,这样才能不遗漏任何可能的疑点。

先来看看swap是如何触发的?

Linux会在两种场景下触发内存回收,一种是在内存分配时发现没有足够空闲内存时会立刻触发内存回收;另一种是开启了一个守护进程(kswapd进程)周期性对系统内存进行检查,在可用内存降低到特定阈值之后主动触发内存回收。

通过如下图示可以很容易理解,详细信息参见:

http://hbasefly.com/2017/05/24/hbase-linux/

图片

是不是只要开启了swap交换区的JVM,在GC的时候都会耗时较长?

笔者去查了一下另外的一个应用,相关指标信息请见下图。

实名服务的QPS是非常高的,同样能看到应用了swap,GC平均耗时 576ms,这是为什么呢?

图片

图片

通过把时间范围聚焦到发生GC的某一时间段,从监控指标图可以看到swapUsed没有任何变化,也就是说没有swap活动,进而没有影响到垃级回收的总耗时。

图片

图片

通过如下命令列举出各进程swap空间占用情况,很清楚的看到实名这个服务swap空间占用的较少(仅54.2MB)

图片

另一个显著的现象是实名服务Full GC间隔较短(几个小时一次),而我的服务平均间隔2周一次Full GC

图片

图片

基于以上推测

  1. 实名服务由于 GC 间隔较短,内存中的东西根本没有机会置换到swap中就被回收了,GC的时候不需要将swap分区中的数据交换回物理内存中,完全基于内存计算,所以要快很多

  2. 将哪些内存数据置换进swap交换区的筛选策略应该是类似于LRU算法(最近最少使用原则)

为了证实上述猜测,我们只需跟踪swap变更日志,监控数据变化即可得到答案,这里采用一段shell 脚本实现

#!/bin/bash 
echo -e `date +%y%m%d%H%M%S` 
echo -e "PID\t\tSwap\t\tProc_Name" #拿出/proc目录下所有以数字为名的目录(进程名是数字才是进程,其他如sys,net等存放的是其他信息) 
for pid in `ls -l /proc | grep ^d | awk '{ print $9 }'| grep -v [^0-9]` 
do if [ $pid -eq 1 ];then continue;fi grep -q "Swap" /proc/$pid/smaps 2>/dev/null if [ $? -eq 0 ];then swap=$(gawk '/Swap/{ sum+=$2;} END{ print sum }' /proc/$pid/smaps) #统计占用的swap分区的 大小 单位是KB proc_name=$(ps aux | grep -w "$pid" | awk '!/grep/{ for(i=11;i<=NF;i++){ printf("%s ",$i); }}') #取出进程的名字 if [ $swap -gt 0 ];then #判断是否占用swap 只有占用才会输出 echo -e "${pid}\t${swap}\t${proc_name:0:100}" fi fi
done | sort -k2nr | head -10 | gawk -F'\t' '{ #排序取前 10 pid[NR]=$1; size[NR]=$2; name[NR]=$3; 
} 
END{ for(id=1;id<=length(pid);id++) { if(size[id]<1024) printf("%-10s\t%15sKB\t%s\n",pid[id],size[id],name[id]); else if(size[id]<1048576) printf("%-10s\t%15.2fMB\t%s\n",pid[id],size[id]/1024,name[id]);else printf("%-10s\t%15.2fGB\t%s\n",pid[id],size[id]/1048576,name[id]); } 
}

由于上面图中 2022.3.2 19:57:00 至 2022.3.2 19:58:00 发生了一次Full GC,我们重点关注下这一分钟内swap交换区的变化即可,我这里每10s做一次信息采集,可以看到在GC时点前后,swap确实没有变化

图片

通过上述分析,回归本文核心问题上,现在看来我的处理方式过于激进了,其实也可以不用关闭swap,通过适当降低堆大小,也是能够解决问题的。

这也侧面的说明,部署Java服务的Linux系统,在内存分配上并不是无脑大而全,需要综合考虑不同场景下JVM对Java永久代 、Java堆(新生代和老年代)、线程栈、Java NIO所使用内存的需求。

总结

综上,我们得出结论,swap和GC同一时候发生会导致GC时间非常长,JVM严重卡顿,极端的情况下会导致服务崩溃。

主要原因是:JVM进行GC时,需要对对应堆分区的已用内存进行遍历,假如GC的时候,有堆的一部分内容被交换到swap中,遍历到这部分的时候就须要将其交换回内存;更极端情况同一时刻因为内存空间不足,就需要把内存中堆的另外一部分换到SWAP中去,于是在遍历堆分区的过程中,会把整个堆分区轮流往SWAP写一遍,导致GC时间超长。线上应该限制swap区的大小,如果swap占用比例较高应该进行排查和解决,适当的时候可以通过降低堆大小,或者添加物理内存。

因此,部署Java服务的Linux系统,在内存分配上要慎重。

以上内容希望可以起到抛转引玉的作用,如有理解不到位的地方烦请指出。

好了,本文的技术部分就到这里啦。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

需求并行开发场景,如何高效发布

云布道师 微服务架构下&#xff0c;每个应用服务独立开发、独立发布&#xff0c;小步快跑&#xff0c;持续快速交付业务需求。多人协同开发同一个应用时&#xff0c;分支开发模式是一个适合的协同方案。该模式下一个需求或任务通常对应一个 feature 分支&#xff0c;多个需求一…

WSL2安装+深度学习环境配置

WSL2安装深度学习环境配置 1 安装WSL22 配置深度学习环境1.1 设置用户名、密码1.2 安装cuda修改WSL安装路径 1.3 安装Anaconda 参考&#xff1a;搭建Windows Linux子系统&#xff08;WSL2&#xff09;CUDA环境 参考&#xff1a;深度学习环境配置 WindowsWSL2 1 安装WSL2 WSL …

《计算机程序的构造和解释》:计算思维的圣经,引领你走向编程的巅峰

写在前面 《计算机程序的构造和解释》&#xff08;Structure and Interpretation of Computer Programs&#xff0c;简记为SICP&#xff09;是MIT的基础课教材&#xff0c;出版后引起计算机教育界的广泛关注&#xff0c;对推动全世界大学计算机科学技术教育的发展和成熟产生了…

【QT】自定义控件的示例

自定义控件&#xff08;很重要&#xff09; 什么是自定义控件&#xff1f; 顾名思义就是创建一个窗口&#xff0c;放入多个控件&#xff0c;拼接起来&#xff0c;一起使用。 为什么需要它&#xff1f; 需求&#xff0c;假设有100个窗口&#xff0c;那如果有两个控件同时被使…

JavaWeb - 1 - 概述

一.什么是Web&#xff1f; Web&#xff1a;全球广域网&#xff0c;也称为万维网&#xff08;www World Wide Web&#xff09;&#xff0c;能够通过浏览器访问的网站 二.Web网站的工作流程 三.Web网站的开发模式 3.1 前后端分离开发&#xff08;主流&#xff09; 3.2 混合开发…

PaddleSeg分割框架解读[01] readme解读

简介 PaddleSeg是基于飞桨PaddlePaddle的端到端图像分割套件,内置45+模型算法及140+预训练模型,支持配置化驱动和API调用开发方式,打通数据标注、模型开发、训练、压缩、部署的全流程,提供语义分割、交互式分割、Matting、全景分割四大分割能力,助力算法在医疗、工业、遥…

MooC下载pdf转为ppt后去除水印方法

1、从MooC下载的课件&#xff08;一般为pdf文件&#xff09;可能带有水印&#xff0c;如下图所示&#xff1a; 2、将pdf版课件转为ppt后&#xff0c;同样带有水印&#xff0c;如下图所示&#xff1a; 3、传统从pdf中去除水印方法不通用&#xff0c;未找到有效去除课件pdf方法…

2024年Android开发陷入饱和,安卓面试题2024中高级

点击领取完整开源项目《安卓学习笔记总结最新移动架构视频大厂安卓面试真题项目实战源码讲义》 前言 这是一篇软文、但是绝对不是鸡汤&#xff1b;为啥不是呢&#xff1f;因为我文笔太差…偶尔矫情发发牢骚&#xff08;勿喷&#xff09; 说说程序猿行业 现在社会上给IT行业…

香港媒体发稿:【超值1元港媒发稿套餐】推广技巧-华媒舍

在当今竞争激烈的市场中&#xff0c;品牌的推广是企业取得成功的关键。众多的宣传渠道中&#xff0c;香港媒体发稿无疑是一种高效的品牌推广方式。本文将为您介绍《超值1元港媒发稿套餐》的各个组成部分&#xff0c;以及它如何帮助您实现品牌的腾飞。 1. 1元套餐的优势 1元港媒…

从《繁花》看图数据库的关联力!

2024年开年&#xff0c;最热的电视剧非《繁花》莫属。 这部现象级剧集不仅在全国掀起了一股怀旧潮&#xff0c;还引发了对故事情节和人物关系的深入探讨。 随着《繁花》的热播&#xff0c;不少观众为了更好地理解复杂的故事情节&#xff0c;开始自制人物关系图。 这些关系图以…

小白跟做江科大51单片机之AT24C02数据存储

1.导入Delay、key、LCD1602相关文件 2.控制逻辑 以I2C去写入&#xff0c;用AT24C02包装好&#xff0c;main调用即可 3.编写I2C代码 看着这六个状态编 图1 开和关 图2 发送一个字节 图3 接收一个字节 图4 接收和发送应答 #include <REGX52.H> sbit I2C_SCL P2^1; sbi…

java: No enum constant javax.lang.model.element.Modifier.SEALED报错

这里我的idea版本为2021.03&#xff0c;JDK版本为21.0.2。经过大量冲浪后大多数都是让修改JDK版本&#xff0c;原因是Modifier.SEALED是JDK15新增的&#xff0c;但是当我修改完JDK版本后并无卵用。 尝试在代码中声明&#xff0c;也没问题可以引用到&#xff0c;这就怪了&#…

CleanMyMac X2024非常值得推荐的Mac清理工具

经过全面的评估和分析&#xff0c;CleanMyMac X在Mac清理和优化软件市场中表现出色。它具备全面的功能特点&#xff0c;如智能扫描、恶意软件检测和清除、应用程序管理等&#xff0c;为用户提供了全面的Mac维护服务。同时&#xff0c;该软件的用户体验也非常优秀&#xff0c;界…

所有人都告诉我考上了会怎么怎么样,却没人告诉我考研失败后该何去何从?

“所有人都告诉我考上了会怎么怎么样&#xff0c;却好像没人告诉我考研失败后该何去何从?” 2023年考研人数: 474万。 在竞争愈发激烈的考研热潮下&#xff0c;破茧成蝶&#xff0c;金榜题名注定只能成为少数人的如愿以偿。梦醒时分&#xff0c;大多数人或许也只能无奈地自嘲&…

【产品经理方法论——产品的基本概念】

1. 产品学三元素 产品学有三个元素&#xff1a;用户、需求、产品 产品学的内容&#xff1a;根据用户的需求设计产品&#xff0c;使用产品服务用户 仅仅通过三个元素无法说明每个元素的概念&#xff0c;因为三个元素互为说明关系。 通过引入人/群体来说明三个元素的关系。 需…

基于springboot海滨学院班级回忆录的设计与实现论文

海滨学院班级回忆录 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了海滨学院班级回忆录的开发全过程。通过分析海滨学院班级回忆录管理的不足&#xff0c;创建了一个计算机管理海滨学院班级回忆录的方案。文章…

2.Rust变量

变量的声明 let关键字 在Rust中变量必须要先声明才能使用&#xff0c;let关键字用于声明变量并将一个值绑定到该变量上。如下: fn main() {let var_name:i32 123123;println!("{}",var_name) //println! 是一个宏&#xff08;macros&#xff09;&#xff0c;可以…

PSINS工具箱笔记——SINS/GNSS的例程

如图&#xff0c;三个不同维数的SINS/GPS的融合&#xff0c;153表示状态量15维、观测量3维&#xff0c;以此类推。 15维包括&#xff1a;失准角&#xff08;3&#xff09;速度误差&#xff08;3&#xff09;位置误差&#xff08;3&#xff09;陀螺漂移&#xff08;3&#xff…

FL Studio20官方中文国内版升级下载2024最新

FL Studio 20&#xff0c;也被众多音乐制作人亲切地称为“水果20”&#xff0c;是一款功能强大的音乐制作软件&#xff0c;它在音乐创作、编曲、录音、混音以及后期制作等方面都有着出色的表现。这款软件不仅适用于专业的音乐制作人&#xff0c;也对初学者非常友好&#xff0c;…

【CSS】关于文字的换行问题

给固定宽度的div中写入一些文字&#xff0c;你会发现文字可能会超出&#xff0c;实例如下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&qu…