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

news2024/9/25 21:23:03

一、现象

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.coloradmin.cn/o/1133713.html

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

相关文章

密码学-SHA-1算法

实验七 SHA-1 一、实验目的 熟悉SHA-1算法的运行过程,能够使用C语言编写实现SHA-1算法程序,增 加对摘要函数的理解。 二、实验要求 (1)理解SHA-1轮函数的定义和工作过程。 (2)利用VC语言实现SHA- 1算法。 (3)分析SHA- 1算法运行的性能。 三、实验…

统计文本词频的几种方法(Python)

目录 1. 单句的词频统计 2. 文章的词频统计 方法一:运用集合去重方法 方法二:运用字典统计 方法三:使用计数器 词频统计是自然语言处理的基本任务,针对一段句子、一篇文章或一组文章,统计文章中每个单词出现的次数…

HttpClient远程使用大全

一 HttpClient简介 1.1 概述 HttpClient只能以编程的方式通过其API用于传输和接受HTTP消息。主要实现功能: 实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等) 支持 HTTPS 协议 支持代理服务器(Nginx…

语法复习之C语言与指针

内存是如何存储数据的? 在C语言中定义一个变量后,系统就会为其分配内存空间。这个内存空间包括了地址和长度。将变量赋值后,该值就被写入到了指定的内存空间中。内存空间的大小一般以字节作为基本单位。   普通变量存放的是数据&#xff0c…

75 应急响应-数据库漏洞口令检索应急取证箱

必须知识点 1.第三方应用由于是选择性安装,如何做好信息收集和漏洞探针也是获取攻击者思路的重要操作,除去本身漏洞外,提前预知或口令相关攻击也要进行筛选 2.排除三方应用攻击行为,自查漏洞分析攻击者思路,人工配合工…

Qt 项目实战 | 多界面文本编辑器

Qt 项目实战 | 多界面文本编辑器 Qt 项目实战 | 多界面文本编辑器界面设计创建子窗口类实现菜单的功能更新菜单状态与新建文件操作实现打开文件操作添加子窗口列表实现其他菜单功能 完善程序功能保存窗口设置自定义右键菜单其他功能 小结项目源码 官方博客:https://…

初试Shiro

Shiro是一个用于身份验证、授权和会话管理的Java安全框架。它提供了一套易于使用的API&#xff0c;可以帮助开发人员构建安全性强大的应用程序。 环境准备 添加依赖 <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</ar…

vueDay04——v-if else show

一、v-if的使用 我们可以像c语言一样去使用v-if结构 比如单用v-if&#xff0c;连用v-if v-else&#xff0c;或者是v-if v-else-if v-else 注意&#xff1a; 1.v-if v-else-if需要绑定值,而v-else不需要绑定值 2.if结构可以用在不同的标签类型之间 <div v-if"fir…

【MATLAB源码-第57期】基于matlab的IS95前向链路仿真,输出误码率曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 IS-95&#xff0c;也被称为cdmaOne&#xff0c;是第一代的CDMA&#xff08;Code Division Multiple Access&#xff0c;码分多址&#xff09;数字蜂窝通信标准。IS-95的全称是Interim Standard-95&#xff0c;最初由Qualcomm…

灵活、可用、高扩展,EasyMR 带来全新 Yarn 的队列管理功能及可视化配置

YARN&#xff08;Yet Another Resource Negotiator&#xff09;是 Hadoop 生态系统中的资源调度器&#xff0c;主要用于资源管理和作业调度。YARN 自身具备队列管理功能&#xff0c;通过对 YARN 资源队列进行配置和管理&#xff0c;实现集群资源的分配&#xff0c;以满足不同应…

Doceker-compose——容器群集编排管理工具

目录 Docker-compose 1、Docker-compose 的三大概念 2、YAML文件格式及编写注意事项 1&#xff09;使用 YAML 时需要注意下面事项 2&#xff09;ymal文件格式 3&#xff09;json格式 3、Docker Compose配置常用字段 4、Docker-compose的四种重启策略 5、Docker Compos…

[微服务]Spring Cloud Sleuth全链路追踪

Spring Cloud Sleuth全链路追踪 全链路追踪组件追踪的数据遵循的规则: 1,Span:基本单元; 执行一次服务调用就生成一个span,用于记录当时的情况 ,以一个64位ID作为唯一标识.span还有其他数据标识如摘要,时间戳信息,关键tag等; 2,Trace:一次请求; 以一个64位ID为唯一标识,可以…

各品牌PLC存储器寻址的规则

在PLC编程时&#xff0c;字节或多字节的变量一般支持绝对地址寻址&#xff08;比如&#xff0c;IW0、MD4等&#xff09;。要想正确寻址&#xff0c;则必须要搞清楚寻址的规则。目前常见的规则有两种&#xff1a;字节寻址和字寻址。下图清晰地表达了两种规则的编号情况&#xff…

AI:WEB 1 靶机

1.找ip地址 2.检测端口 3.上文件检索 发现是apache 的组件 4.漏洞扫描攻击 nikto -h 网站 扫描网站漏洞 目录爆破 5.利用发现敏感目录 6.登录 发现 都是403 使用上层发现ok 这是一个查询功能点 7.敏感目录 sql注入 检验 详细信息 检测到注入方式 查看数…

系统架构设计师之RUP软件开发生命周期

系统架构设计师之RUP软件开发生命周期

EasyAR使用

EazyAR后台管理&#xff0c;云定位服务 建模 需要自行拍摄360度视频&#xff0c;后台上传&#xff0c;由EazyAR工作人员完成构建。 标注数据 需要在unity安装EazyAR插件&#xff0c;在unity场景编辑后&#xff0c;上传标注数据。 uinity标注数据 微信小程序中使用&#x…

Kali Linux 安装使用远程桌面连接远程服务器

1. 安装远程桌面连接软件 目前为止&#xff0c;我们已经可以通过其它机器以远程桌面的方式连接 Kali Linux&#xff0c;但想要使用 Kali Linux 远程连接其它机器还无法做到&#xff0c;下面就看看如何实现。 更新软件源列表&#xff1b; $ sudo apt-get update $ sudo apt-g…

二、可行性分析与需求分析

文章目录 概念考点练习题一、可行性分析与需求分析1.可行性分析的任务2.可行性研究3.甘特图4.数据流图5.数据字典数据字典的内容 6.需求分析7. 实体联系ER图8. 状态转换图 二、练习题 概念考点练习题 一、可行性分析与需求分析 1.可行性分析的任务 用最小的代价在尽可能短的时…

Xilinx MicroBlaze定时器中断无法返回主函数问题解决

最近在使用Xilinx 7系列FPGA XC7A100T时&#xff0c;运行MicroBlaze软核处理器&#xff0c;添加了AXI TIMER IP核&#xff0c;并使能定时器溢出中断&#xff0c;发现定时器触发中断后&#xff0c;无法返回主函数的问题&#xff0c;最后发现修改编译器优化等级就正常了。 FPGA型…

量子力学的基础公设

量子力学的基础公设 - 知乎