排查和解决JVM OOM实战

JVM OOM介绍

Java内存区域布局

下面的分析中都是基于JDK 8开始的。关于JMM不过多介绍每个区域的作用。OOM不单只会发生在堆内存,也可能是因为元空间或直接内存泄漏导致OOM,此时在OOM的详细信息中会有不同体现。

Java OOM的类别

java.lang.OutOfMemoryError: GC overhead limit exceeded

JVM 98%的CPU时间都在执行GC,并且每次GC回收空间小于堆内存的2% 基本就是应用不可用的状态了!

可能原因:1.堆内存设置过小 2.内存泄漏

java.lang.OutOfMemoryError: Java heap space

JVM 堆内存无法再分配对象。

可能原因:1.堆内存设置过小 2.内存泄漏 3.对象实现finalize方法不当,回收速度跟不上finalization queue入队速度

java.lang.OutOfMemoryError: Metaspace

元空间是直接用本地内存,受限于MaxMetaSpaceSize参数。存放的类对象信息过多导致元空间溢出。

可能原因:1.元空间过小 2.动态代理造成类对象过多

java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?

很少出现。通常是JVM的本地堆内存无法再分配对象导致。

java.lang.OutOfMemoryError: Compressed class space

很少出现。

java.lang.OutOfMemoryError: reason stack_trace_with_native_method

很少出现。摘自Orcale JDK Document

If the detail part of the error message is "reason stack_trace_with_native_method" and a stack trace is printed in which the top frame is a native method, then this is an indication that a native method has encountered an allocation failure. The difference between this and the previous message is that the allocation failure was detected in a Java Native Interface (JNI) or native method rather than in the JVM code.

沃尔玛支付中台实际案例

我在沃尔玛中国的科技部工作的时候,负责过支付中台的研发,我接手的时候还是一个刚起步的系统。我负责的一年半时间中,接入门店从2家扩展到全国200家门店。日支付单量从7、8千到80~100万的规模。为全国的山姆会员店自助收银机、沃尔玛大卖场自助收银机、线上山姆会籍系统提供支付退款结算能力。中间的过程不是一帆风顺,在一个元旦假期还遇到OOM的线上事故(后面排查出是前同事的老代码导致),造成门店大面积停用自助收银机,顾客被迫转向人工收银和小程序线上缴费等其它支付通道。

问题表现

第一次发生OOM问题时候,排查日志发现OOM前十分钟,日志显示请求进来的线程都在运行到某个时刻全部被hoding。再也没有日志输出,原本业务代码中正常时刻会输出日志。直到OOM错误出现。整个服务表现为不可用状态。后来导出的jstack日志显示所有线程都是BLOCKED状态。观察日志发现线程号貌似增长到50+。Kevin判断是请求外部接口时遇到了很大的延迟导致线程没释放而外部请求堆积越来越多,最终消耗完了内存。

于是按照这个判断给HTTP接口加上限流和告警监控,毕竟没有办法干预外部第三方支付接口。以为事情就解决了,但是......

在元旦假期的时候下午5点支付中台又发生了OOM。6g的堆内存,4g的old gen全部拉满,观察听云old gen从昨天一直满的到现在。查看full gc频率,基本20分钟内发生200+次full gc,查看日志也是显示 GC overhead limit exceeded error 代表应用98%的时间都在执行GC,并且每次GC回收空间小于堆内存的2% 基本就是应用不可用的状态了!

门店也反馈收银支付十分慢,表现就是卡死!周一被军训和被吊打是免不了的了。最后排查发现是前同事擅自改了银联SDK,把CertUtil从静态类改成对象,每次银联请求时都创建对象,直接触发了底层的一个坑。

排查过程

基于基础知识我们知道,OOM的类别中GC overhead limit exceeded error,很大概率是老年代堆内存发生了泄漏,导致GC一直没办法有效释放对象,进而引发不断地Full GC,整个服务用户进程的CPU时间都用在GC线程上了,用户线程反而得不到CPU时间,服务就假死了。立即新启动几个服务节点顶上,新节点内存会缓慢的上升直至OOM,中间有一段可用时间,可以恢复服务能力。

排查内存泄漏,一般无法从代码看出来哪里泄漏了。所以我们得想办法拿到内存快照,用:jmap -dump:live,format=b,file=filename.bin pid

这个命令可以导出堆内存中存活的对象,因为我们排查内存泄漏所以只要求存活对象。使用前注意:jmap为了收集准确的堆内存使用情况会暂停jvm的用户线程和gc线程。所以造成STW。具体STW时长取决于导出的堆内存大小正相关。

关于jmap是否会造成STW可看stackoverflow一篇文章:

java - Is a JVM stopped while executing jmap? - Stack Overflow

jmap导致stw造成生产事故的例子:

memory - Java : Get heap dump without jmap or without hanging the application - Stack Overflow

其中文章内提了一些避免STW同时获取堆内存快照的做法,例如用gdb命令。fork进程,然后在子进程dump,fork可能会占用大量额外内存在主进程同时对同一个地址空间的内存大量写动作,就会有copy-on-wirte发生,会分配新内存页copy旧的内存页让主进程在新的内存页写入。

以上做法其实都不推荐,也没有实践过。正确且成熟的做法应该是在网关层或负载层把机器节点摘掉,验证没有流量后,再使用jmap导出堆内存快照,这时候你想怎么搞就怎么搞。

当晚等到新节点内存涨到一定规模后,即将发生FGC,摘流用jmap导出内存快照,下载到本地分析。每台服务器两个快照,一共4个。最大的快照8.4G。把快照下到本地,用MAT打开分析。提前要调整好MAT的maxHeapSize,不然MAT无法装载快照。

把快照导入后,8.4G大概加载了30分钟,然后就按照提示选择 Leak Suspects 分析,完毕后会给出内存对象占用空间的饼图,并且给出 problem a b 等推测泄漏对象/类。基本逐个查看泄漏对象的details,例如看这个对象的dominator tree等等

使用MAT分析泄漏点

本次事故中,在Problem Suspect 中很明显可以看到是一个 JceSecurity对象占了5.2G内存,这肯定是不正常的。果然就是内存泄漏了。details中在支配树中看到JceSecurity有一个IdentityHashMap内部用的Object[]数组保存Entry。这个数组中有6000+个BouncyCastleProvider对象,总共占了5.2G内存。为什么这个对象不会被回收?进一步看这个对象的 Path to GC roots,排除所有弱引用、虚引用。可以看到gc root就是JceSecurity对象,这个对象MAT提示是System class,是一个GC root不会被回收的。

结合源码看,JceSecurity里的map是一个static final修饰,map是常量的,也就是6000+多个provider对象被map持有强引用所以一直释放不了。

OK!这就很明显的泄漏就是这个provider对象了,但是这里只找到了泄漏对象。这个对象我也不认识,也不记得代码里哪里用到了它。

根据网上的教程,也没通过MAT查找到源码级别哪个地方泄漏了provider,幸运地在代码里全局搜索BouncyCastleProvider,很快在银联的SDK中找到这个对象的使用。并且是Security.add(new BouncyCastleProvider()) 这样明显的代码。再看这段代码是每次创建CertUtils对象会执行的代码。很自然怀疑是这个地方,于是在本地复现,简单的死循环调用 Security.add(new BouncyCastleProvider()),通过VMVisual监控JVM进程的堆内存变化,很正常的释放,provider对象数量涨到一定程度又降下去了。

难道不是这里?最后在QA服务器,压测银联的查询接口,jstat监控堆内存的使用量,几十分钟后发现old gen一直无法释放内存,并且开始频繁full gc。dump了内存快照到本地查看,果然和生产的快照显示一样,是JceSecurity占了大部分内存!

那么就可以确定是银联CertUtil的问题,查看官网的原对象使用,人家是静态类,会在static代码块调用一次 Security.add(new BouncyCastleProvider())。但是我们的代码是每次创建CertUtil造成每次调用Security.add(new BouncyCastleProvider())。

但问题是为什么死循环没复现泄漏问题呢?后来查资料可能是没有使用加解密代码,并且用VMVisual查看JceSecurity对象没有生成,虽然产生了很多provider但因为没有强引用,都被GC了。

结果

最后通过修正相关代码,避免每次都add Provider。再次发布QA,压测银联查询接口,dump快照,没有发现大量占内存的对象。jstat观察old gen也基本平缓,没有full gc产生。发布生产环境,跑一段时间再看看。到这里基本可以确定是修复了这个泄漏问题了。

总结排查手段和思路

如果一个服务发生了OOM,要判断是什么类型的OOM。然后对症排查分析。我们日常工作中最常出现的就是堆内存的OOM。先看是内存设置是否合理,其次看是否流量暴增导致,最后要怀疑内存泄漏。

如果是内存泄漏,可以通过jstat先简单查看GC情况,表现应为old区经历了FGC或old gc仍然无法释放内存,并且连续每次几乎都是这种情况,就可以高度怀疑是内存泄漏。通过摘流、jmap导快照、MAT分析、源码分析一套连招下来,大部分内存泄漏可以被定为到泄漏点然后修复。

找到泄漏点后更严谨的,需要在本地环境或QA环境进行复现。然后修复后再次进行复现,验证是否修复。

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

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

相关文章

王者农药更新版

一、启动文件配置 二、GPIO使用 2.1基本步骤 1.配置GPIO,所以RCC开启APB2时钟 2.GPIO初始化(结构体) 3.给GPIO引脚设置高/低电平(WriteBit) 2.2Led循环点亮(GPIO输出) 1.RCC开启APB2时钟。…

HarmonyOS/OpenHarmony 自定义弹窗页面级层级控制解决方案

关键词:CuntomDialog自定义弹窗、SubWindow子窗口、页面级、弹窗层级控制、鸿蒙、弹窗展示层级异常 问题存在API版本:API10 - API12(该问题已反馈,期望后续官方能增加页面级控制能力) 在正常的鸿蒙app开发过程中&…

TIM(Timer)定时器的原理

一、介绍 硬件定时器的工作原理基于时钟信号源提供稳定的时钟信号作为计时器的基准。计数器从预设值开始计数,每当时钟信号到达时计数器递增。当计数器达到预设值时,定时器会触发一个中断信号通知中断控制器处理相应的中断服务程序。在中断服务程序中&a…

LeetCode讲解篇之239. 滑动窗口最大值

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们维护一个长度为k的窗口,然后窗口从数组最左边一直移动到最右边,记录过程中窗口中的最大值,就是答案 我们每次查询长度为k的窗口最大值是什么时间复杂度是O(k)的&#xff0…

rust中async/await的使用

在Rust中,async/await 用于编写异步代码。它允许您以同步的方式编写异步代码,使得异步操作更易于理解和编写。 安装依赖: cargo add futures cargo add async-std 使用示例: 示例1: use async_std::task::block_on;fn main() {block_on(hello()); }async fn hello() …

MoveIt2-humble----Planning Around Objects

1 添加Planning Scene Interface头文件 #include <moveit/planning_scene_interface/planning_scene_interface.h>2 改变目标位姿 // Set a target Pose auto const target_pose [] {geometry_msgs::msg::Pose msg;msg.orientation.w 1.0;msg.position.x 0.28;msg.p…

Github 2024-10-06 php开源项目日报 Top10

根据Github Trendings的统计,今日(2024-10-06统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Blade项目2Laravel:表达力和优雅的 Web 应用程序框架 创建周期:4631 天开发语言:PHP, BladeStar数量:75969 个Fork数量:24281 次…

Linux安装部署MySQL8.0加遇着问题解决

1.首先我先给个URL下载MySQL官方网站https://downloads.mysql.com/archives/community/ 2.选择Linux的红帽系统 3.接着选择红帽系统的7版本,x86 4.接着选择MySQL版本,此时我选择8.4.0,下载rpm bundle这个,下载下面这个就好 5.Windows文件上传到Linux系统 rz上传文件命令,找到…

【unity游戏开发】彻底理解AnimatorStateInfo,获取真实动画长度

前言 前置知识&#xff1a;设置参数后&#xff0c;下一个循环才会切换对应动画&#xff0c;所以在下一个循环获取真实的动画长度 AnimatorStateInfo是结构体&#xff01;值类型&#xff0c;要不断重复获取才是最新的 主要是自动设置trigger切换的动画自动切回上一个动画&#x…

Java中如何实现定时任务?

目录 一、定时任务 概念 作用 二、简单定时任务实现方式 1. Thread线程等待&#xff08;最原始最简单方式&#xff09; 2. 使用java.util.Timer Timer 优缺点分析 3. 使用JDK自带的ScheduledExecutorService schedule和scheduleAtFixedRate的区别 schedule侧重保持间隔…

B 私域模式升级:开源技术助力传统经销体系转型

一、引言 1.1 研究背景 随着市场竞争加剧&#xff0c;传统经销代理体系面临挑战。同时&#xff0c;开源技术发展迅速&#xff0c;为 B 私域升级带来新机遇。在当今数字化时代&#xff0c;企业面临着日益激烈的市场竞争。传统的经销代理体系由于管理效率低下、渠道局限、库存压…

贝锐蒲公英网盘首发,秒建私有云,高速远程访问

虽然公共网盘带来了不少便利&#xff0c;但是大家对隐私泄露和重要数据泄密的担忧也随之增加。如果想要确保数据安全&#xff0c;自建私有云似乎是一条出路&#xff0c;然而面对搭建私有云的复杂步骤&#xff0c;许多人感到力不从心&#xff0c;NAS设备的成本也往往让人望而却步…

项目——超级马里奥——Day(2)

争取今天晚上能搞一半啊&#xff0c;啊啊啊啊&#xff0c;感觉事多的忙不过来 设计思路&#xff1a; 1&#xff09;创建并完成常量类 ------->一张图片的情况 先完成对图片的封装------>把图片加载一遍 &#xff08;老实说&#xff0c;我也不太知道为什么&#xff0…

【项目安全设计】软件系统安全设计规范和标准(doc原件)

1.1安全建设原则 1.2 安全管理体系 1.3 安全管理规范 1.4 数据安全保障措施 1.4.1 数据库安全保障 1.4.2 操作系统安全保障 1.4.3 病毒防治 1.5安全保障措施 1.5.1实名认证保障 1.5.2 接口安全保障 1.5.3 加密传输保障 1.5.4终端安全保障 资料获取&#xff1a;私信或者进主页。…

约数个数约数之和

好久没发文章了.......不过粉丝还是一个没少...... 今天来看两道超级恶心的数论题目&#xff01; No.1 约数个数 No.2 约数之和 先来看第一道&#xff1a;约数个数 题目描述 给定 n 个正整数 ai​,请你输出这些数的乘积的约数个数,答案对 10^97 取模 输入格式 第一行包含…

OBOO鸥柏丨OLED透明触摸查询一体机数字科技触控广告屏技术前沿

吊挂透明OLED触摸屏一体机正成为博物馆数字化展示的“共同奔赴赛道选择&#xff0c;透过透明屏幕看到展示物品的内部结构和细节&#xff0c;GG纯平面触控实现展示查询交互与互动的完美结合。相比传统的商用/工业液晶显示屏机柜&#xff0c;OLED透明触摸屏具有更高的对比度和更广…

佳易王电玩店ps5计时计费系统软件倒计时语音提醒软件操作教程

一、前言 【试用版软件下载可以点击最下方官网卡片】 佳易王电玩店ps5计时计费系统软件倒计时语音提醒软件操作教程 1、时间显示&#xff1a;正常使用时间&#xff0c;直观显示在对应桌旁。 2、倒计时显示&#xff1a;右侧显示剩余多少分钟&#xff0c; 3、定时语音提醒&am…

阿里云域名注册购买和备案

文章目录 1、阿里云首页搜索 域名注册2、点击 控制台3、域名控制台 1、阿里云首页搜索 域名注册 2、点击 控制台 3、域名控制台

YOLOv11改进,YOLOv11添加DCNv4可变性卷积(windows系统成功编译),二次创新C2f结构,全网最详细教程

改进训练结果前: 二次创新C2f结构训练结果: 摘要 引入了可变形卷积 v4 (DCNv4),这是一种为广泛视觉应用设计的高效且有效的操作算子。DCNv4通过两项关键增强解决了其前身DCNv3的局限性:1. 移除空间聚合中的softmax归一化,以增强其动态特性和表达能力;2. 优化内存访问以…

Task与 async 和await关键字使用和探讨

基本概念&#xff1a; Task (任务): 在 .NET 中&#xff0c;Task 表示一个可能会在未来完成的操作&#xff0c;可以是异步的&#xff0c;也可以是同步的。Task<TResult> 是返回结果的任务&#xff0c;而 Task 是不返回结果的任务。async 关键字: 标记一个方法为异步方法…