生产环境元空间内存溢出(OOM)的问题排查

一、现象

2023.10.17下午收到业务反馈,说是接口调用超时,进件系统和核心系统调用外数系统接口时等待过久,引起系统异常。然后我们看了下接口调用的日志,确实接口的响应时间在五十秒左右。我们自己测试了下,发现也是这么长,这是必现的,大概率就是系统自身的问题。很容易就联想到Java虚拟机内在频繁gc,导致Java进程运行受阻,处理时间过长,然后我们就看到报元空间内存溢出的异常了:java.lang.OutOfMemoryError: Metaspace,当时我们调大元空间的内存就立即重启了。

 

二、排查过程

1.相关概念

Java虚拟机的元空间是保存类的元数据(Class)的,不同于堆,元空间是到服务器上申请内存的,所以如果一台服务器部署了多个应用,最好设置个上限,可以通过这两个参数设置大小:

  1. -XX:MetaspaceSize=256m //初始大小
  2. -XX:MaxMetaspaceSize=512m //最大值

我们生产设置的值为512M,正常情况下也是够用的,测试环境压测的时候发现元空间内存稳定在220M左右,说明加载了很多类。

Jdk内置了很多工具jcmd、jconsole、jvisualvm,配合下面这些参数,可以查看Java虚拟机内存占用情况:

  1. -XX:NativeMemoryTracking=summary //NMT内存分析
  2. -XX:+UnlockDiagnosticVMOptions //启用内置工具
  3. -XX:+TraceClassLoading //查看类加载信息
  4. -XX:+TraceClassUnloading //查看类卸载信息

反射调用时会加载很多中间类,用于构造Java对象或方法调用。使用反射应该慎重,并且只在没有其他代替方案时使用。

2.定位问题

从日志看,首先com.netflix.discovery.DiscoveryClient是eureka客户端发现注册中心的类,eureka客户端启动时会开启定时任务周期性轮询微服务的状态,本质就是发送get请求拉取微服务信息并与本地已经保存的微服务进行对比,发现有变化就更新,这里面会执行一段代码:

可见每次轮询都会创建RequestExecutor接口的匿名实现类,如果元空间满了,就会报内存溢出的异常,这是在DiscoveryClient类的fetchRegistry方法中报的,日志中的Timeout则是因为无法创建匿名类导致Future无法返回,这样就能明确的定位是由于元空间内存满了导致频繁gc,而引起的线上问题。

  1. 复现问题

刚开始我们觉得是线上的并发量很大,处理的请求过多,导致我们的反射代码大量执行,从而加载了很多中间类,于是我们进行了压测,结果发现元空间大概稳定在220M左右,然后每次波动很小,上涨很慢。之后去生产看了接口调用信息,发现qps在39左右,因此不是高并发引起。之后我们仔细看了日志,发现在之前就一直报主键冲突的异常。我们用的是Oracle数据库,主键是通过雪花算法生成的。

这里我想到两个问题,一是不断地报主键冲突的异常对元空间内存占用造成的影响是什么,二是为什么会报主键冲突的异常。

a.反射对元空间的影响

Java虚拟机底层是c++实现的,代码看不懂的。网上的说法也是众说纷纭,每个统一的标准,而且Java内存溢出也是很少出现。我就打算自己来验证了。首先我到生产环境把出现主键冲突到内存溢出这段时间的接口都查出来了,然后使用postman为接口设置调用次数,延时为25ms,模拟qps为40的调用环境。在本地的代码中手动设置实体类主键为数据库中已有的主键,结果就复现了生产环境内存溢出的问题了。

我通过 jcmd  220096  GC.class_stats  >> class10.txt 指令将虚拟机的类加载信息保存了下来,可以看到加载了大量的sun.reflect包下的类,这些正是跟反射相关的类,用于反射的方式构造实例对象和方法调用。从日志也可以看到,如果jdbc抛出了异常,就会有大量的发射调用。

所以我得出的结论就是我们元空间内存溢出(OOM)就是由不断地产生主键冲突的异常引起的。

b.雪花算法

我们的主键不是数据库生产,而是我们通过雪花算法生成的。我先看了实体类中id所有set方法调用的地方,发现这个值只来自于雪花算法的实现方法。

我们的id是由generate()方法生成的,雪花算法是由nextId()实现的,timeStr是"yyMMddHHmmss"格式表示的时间字符串,这个方法的逻辑就是如果同一秒的线程拿到锁执行nextId()方法序列号就会加加,到新的一秒就会置0,而我们的id就是由"yyMMddHHmmss"格式表示的时间字符串和nextId()方法返回的值合并的,所以一般情况应该是不会重复的。

但也有特殊情况,第一是服务器的时间变化了,就会生成重复的id,虽然这个概率很低。第二个就是多线程同步执行的情况了,看图说话吧,整个过程分以下几步:

  1. 线程1在12:00:00.990获取到锁执行nextId()方法的时候,线程2在12:00:00.995进来了,线程3在12:00:01.005进来了,假设方法的执行时长为20ms。
  2. 到12:00:01.010方法执行完毕,线程1生成的id为”20231015120000”+”0000”,此时释放锁;
  3. 由于synchronized是公平锁,所以要么线程2获取到锁,要么线程3获取到锁,如果线程2获取到生成的id就是”20231015120000”+”0001”,再然后线程3拿到锁生成的id是”20231015120001”+”0000”,id是各不相同的;但是如果是线程3先拿到锁呢,那么线程3拿到锁后生成id是”20231015120001”+”0000”,线程2再拿到锁生成的id就是”20231015120000”+”0001”,就跟线程1产生的id重复了,也就会在插入到数据库时引起主键冲突了,进而抛出异常,进而持续大量占用元空间,导致元空间内存溢出。

为什么会产生这个问题,因为线程抢占锁是随机且公平,不是按照时间顺序的,就是说可能后一秒的线程执行完了,锁可能会让给前一秒在等待的线程。

  1. 解决问题

刚开始打算使用Atomic原子变量解决,就写个下面这个版本,意思是只要有请求进来

就加加,只要一秒内的请求数不超过MAX_SEQUENCE(8191)就不会重复结果压测发现还是生成的重复的id,上面的nextIdAtomic()效率是很高,但是问题是sequence是单例bean的属性,即便计算出sequence的值,在return之前如果有其他线程进来了拿到的就是跟当前线程一样的值。

最终我们的雪花算法实现如下:

逻辑是每次获取当前毫秒时间戳,如果是同一毫秒的线程序列号就加加,加到512为止(如果加到512以上还是会重复,但是概率几乎为0,因为1s内得有500*60=30000个请求进来),如果不同毫秒的线程就置为0,压测了20万个线程,分别设置qps为100、200、1000均正常。

三、总结

通过这次生产问题的解决,自己也积累了一点排查问题的经验。首先解决问题的时候一定要学会分析,问题是什么,怎么产生的,怎么导致的,要一层一层的排查。然后就是大胆猜想,小心证明,一定要自己动手操作,容易产生灵感。现有的数据一定要好好利用。最后就是平时的积累很重要,因为不怎么熟悉jvm,所以很多工具还是现学现卖的,就比较耽误时间,所以平时不可以忽视学习积累的机会。

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

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

相关文章

leetcode 503. 下一个更大元素 II、42. 接雨水

下一个更大元素 II 给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数&…

【c#】2022创建WEB API接口教程demo

c#创建WEB API接口 创建WEB API接口结果图涉及到的技术设计流程创建WEB API接口 结果图 涉及到的技术 设计流程 1、创建WEB api项目,使用控制器和penapi勾选上,第一次创建项目时没有勾选,因为感觉没啥用。后面跑项目的时候,要把接口用swagger去直接生成的时候,还是需要…

自然语言处理---Transformer机制详解之BERT模型介绍

1 BERT简介 BERT是2018年10月由Google AI研究院提出的一种预训练模型. BERT的全称是Bidirectional Encoder Representation from Transformers.BERT在机器阅读理解顶级水平测试SQuAD1.1中表现出惊人的成绩:全部两个衡量指标上全面超越人类,并且在11种不…

Postman的简单使用

Postman简介 官网 Postman是Google公司开发的一款功能强大的网页调试与发送HTTP请求,并能运行测试用例的Chrome插件 使用Postman进行简单接口测试 新建测试 → 选择请求方式 → 请求URL,下面用百度作为例子: 参考文档 [1] Postman使用教程…

异常的处理和HTTP状态码的分类

在爬虫过程中,可能会遇到各种异常情况,如网络连接错误、网页解析错误、请求超时等。为了提高爬虫的稳定性和容错性,需要对这些异常进行处理。 异常处理是通过捕获和处理异常来解决程序中出现的错误情况。在爬虫中,常见的异常处理…

RCE 远程代码执行漏洞分析

RCE 漏洞 1.漏洞描述 Remote Command/Code Execute 远程命令执行/远程代码执行漏洞 这种漏洞通常出现在应用程序或操作系统中,攻击者可以通过利用漏洞注入恶意代码,并在受攻击的系统上执行任意命令。 2.漏洞场景 PHP 代码执行PHP 代码注入OS 命令执…

Kettle循环结果集中的数据并传入SQL组件【或转换】里面

简介:在尝试使用了结果集的Demo循环后,进入到生产还是有一点问题的,以下是各个组件的分解解释、遇到的问题,以及解决问题的思路,最后文章的最后会把完整的Ktr文件放出来。记得收藏点赞喔! 先来看张图~来自…

OSPF的网络类型

1.3配置OSPF的网络类型 1.3.1实验3&#xff1a;配置P2P网络类型 实验需求 实现单区域OSPF的配置实现通过display命令查看OSPF的网络类型 实验拓扑 实验拓扑如图1-11所示 图1-11 配置P2P网络类型 实验步骤 步骤1&#xff1a;[1] 配置IP地址 路由器R1[2] 的配置 <Huawe…

html iframe 框架有哪些优缺点?

目录 前言&#xff1a; 用法&#xff1a; 理解&#xff1a; 优点&#xff1a; 嵌套外部内容&#xff1a; 独立性&#xff1a; 分离安全性&#xff1a; 跨平台兼容性&#xff1a; 方便维护&#xff1a; 缺点&#xff1a; 性能开销&#xff1a; 用户体验问题&#xf…

vue项目中内嵌iframe,打包上线时候iframe地址如何写?

vue项目中内嵌iframe&#xff0c;打包上线时候iframe地址如何写 一、项目结构1.内嵌的iframe文件位置2.打包后的iframe的位置 二、代码 前提描述&#xff0c;项目是用webpack打包的&#xff0c;内嵌一个完整的js小组件 一、项目结构 1.内嵌的iframe文件位置 2.打包后的iframe的…

图论05-【无权无向】-图的广度优先BFS遍历-路径问题/检测环/二分图/最短路径问题

文章目录 1. 代码仓库2. 单源路径2.1 思路2.2 主要代码 3. 所有点对路径3.1 思路3.2 主要代码 4. 联通分量5. 环检测5.1 思路5.2 主要代码 6. 二分图检测6.1 思路6.2 主要代码6.2.1 遍历每个联通分量6.2.2 判断相邻两点的颜色是否一致 7. 最短路径问题7.1 思路7.2 代码 1. 代码…

听GPT 讲Rust源代码--library/std(2)

File: rust/library/std/src/sys_common/wtf8.rs 在Rust源代码中&#xff0c;rust/library/std/src/sys_common/wtf8.rs这个文件的作用是实现了UTF-8编码和宽字符编码之间的转换&#xff0c;以及提供了一些处理和操作UTF-8编码的工具函数。 下面对这几个结构体进行一一介绍&…

wkhtmltoimage/wkhtmltopdf 使用实践

1. 介绍 wkhtmltopdf/wkhtmltoimage 用于将简单的html页面转换为pdf或图片&#xff1b; 2.安装 downloads 2.1. mac os 下载64-bit 版本然后按照指示安装, 遇到 untrust developers 时&#xff0c;需要在 Settings -> Privacy 处信任下该安装包。 2.2. debian # 可用…

【完美世界】被骂国漫之耻,石昊人设战力全崩,现在真成恋爱世界了

【侵权联系删除】【文/郑尔巴金】 深度爆料&#xff0c;《完美世界》动漫第135集预告片已经更新了&#xff0c;但是网友们对此却是一脸槽点。从预告中可以看出&#xff0c;石昊在和战王战天歌的大战中被打成重伤&#xff0c;最后云曦也被战天歌抓住。在云曦面临生死危机的时候…

SRAM与DRAM的区别

目录 SRAM 特点 应用场景 DRAM 特点 应用场景 SRAM和DRAM的区别 SRAM SRAM&#xff08;静态随机存取存储器&#xff09;是一种用于存储和检索数据的类型的计算机内存。SRAM的存储单元通过触发器&#xff08;flip-flop&#xff09;实现&#xff0c;它们可以保持数据的状态…

linux-文件系统

目录 一、文件系统 1.分区 2.文件系统分类 3.文件系统创建工具 4.查看文件系统的属性 5.挂载 6.buffer和cache 一、文件系统 1.分区 1-4个主分区 第五个序号开始&#xff0c;是逻辑分区 2.文件系统分类 vfs文件系统 ------------- virtualenv file System&#xff0…

Tensorboard安装及简单使用

Tensorboard 1. tensorboard 简单介绍2. 安装必备环境3. Tensorboard安装4. 可视化命令 1. tensorboard 简单介绍 TensorBoard是一个可视化的模块&#xff0c;该模块功能强大&#xff0c;可用于深度学习网络模型训练查看模型结构和训练效果&#xff08;预测结果、网络模型结构…

Uniapp 酷炫钱包页面模板 直接引用

使用教程 直接引用Vue页面 即可 <template><view><TCqianbao></TCqianbao></view> </template> <script>import TCqianbao from /uni_modules/TC-qianbao/pages/index.vueexport default {components:{TCqianbao},} </script&…

UVM 验证方法学之interface学习系列文章(八)《interface不小心引入X态问题》

前面的文章学习,想必大家都对interface 有了深入了解。大家可不要骄傲哦,俗话说:小心驶得万年船。今天,再给大家介绍一个工作中,不是经常遇到,但是一旦遇到,会让你纠结很久的事情。 前面文章提到,随着验证复杂度的不断增加,interface 的bind 的操作,是必不可少的用法…

asp.net网上商城系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio协同过滤设计

一、源码特点 asp.net网上商城系统是一套完善的web设计管理系统系统采用协同过滤算法进行商品推荐&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库 为sqlserver2008&#xff0c;使用c#语言开发 ASP…