Java内存泄露生产环境排查过程,通透了

news2025/5/17 17:23:44

昨天线上环境崩了

java堆内存溢出。。。
报错:java.lang.OutOfMemoryError: Java heap space

下面我将我排查问题的思路和过程记录了下来

1. 场景

  1. 客户端跟Java服务端通过websocket连接建立长链接并发送语音数据(text格式)
  2. Java服务端跟听写引擎建立长链接并发送语音数据
  3. 听写引擎将实时转写的内容返回给Java服务端,Java服务端再将数据处理返回给客户端

2. 排查过程

2.1 通过命令查看异常进程

由于是堆内存溢出异常,所以我先检查了一下服务器的负载和堆内存试用情况

通过 top 命令查看服务器进程情况,发现pid为1的一个java进程cpu使用率达到90%多
在这里插入图片描述
这个进程正好是出现异常的这个java应用

通过 jps 命令也可以查看正在运行的java应用
在这里插入图片描述

2.2 分析堆内存使用情况

接下来查看这个应用的堆内存使用情况
通过 jmap -heap pid 这个命令
在这里插入图片描述
简要的对上图👆中的内存使用情况做简单的说明:

MinHeapFreeRatio = 40
说明:当堆内存的空闲比例低于这个值时,JVM 会尝试增加堆大小。
影响:设置为 40 表示当堆内存的空闲部分小于 40% 时,JVM 会考虑增加堆大小。
MaxHeapFreeRatio = 70
说明:当堆内存的空闲比例高于这个值时,JVM 会尝试减少堆大小。
影响:设置为 70 表示当堆内存的空闲部分大于 70% 时,JVM 会考虑减少堆大小。
MaxHeapSize = 536,870,912 (512.0 MB)
说明:JVM 最大堆内存大小。
影响:这是 JVM 可以使用的最大内存,当前设置为 512 MB。如果应用程序需要更多内存,可能会遇到 OutOfMemoryError。
NewSize = 11,141,120 (10.625 MB)
说明:新生代(Young Generation)的初始大小。
影响:这是新生代的最小容量,用于存储新创建的对象。
MaxNewSize = 178,913,280 (170.625 MB)
说明:新生代的最大容量。
影响:这是新生代可以增长到的最大大小。
OldSize = 22,413,312 (21.375 MB)
说明:老年代(Old Generation)的初始大小。
影响:这是老年代的最小容量,用于存储晋升对象。
NewRatio = 2
说明:老年代与新生代的比例。例如,NewRatio=2 意味着老年代是新生代的两倍大。
影响:调整新生代和老年代的比例,影响垃圾回收的频率和效率。
SurvivorRatio = 8
说明:Eden 区与 Survivor 区的比例。例如,SurvivorRatio=8 意味着 Eden 区是每个 Survivor 区的八倍大。
影响:调整 Eden 和 Survivor 区的大小,影响对象在年轻代中的存活时间。
MetaspaceSize = 21,807,104 (20.796875 MB)
说明:Metaspace 的初始大小,用于存储类元数据。
影响:Metaspace 的初始分配大小,影响类加载的速度。
CompressedClassSpaceSize = 1,073,741,824 (1024.0 MB)
说明:压缩类空间的大小,用于存储类的内部表示。
影响:限制类元数据的存储空间。
MaxMetaspaceSize = 17,592,186,044,415 MB
说明:Metaspace 的最大大小。实际上,这个值非常大,几乎等于无限制。
影响:理论上没有限制,但实际受限于物理内存和操作系统。
G1HeapRegionSize = 0 (0.0MB)
说明:G1 垃圾收集器的区域大小。这里显示为 0,意味着未使用 G1 收集器。
影响:确认当前使用的不是 G1 收集器。

简单分析:
可以很明显的看到新生代和老年代几乎完全被使用(99.99% 和 100%),这意味着几乎没有可用的堆内存

如果此时出现Java heap space很大可能是因为内存使用完 然后又有对象被分配,这个时候没有空间并且在执行垃圾回收的时候没有回收到对象 所以会溢出

可以通过 jstat -gc 命令查看垃圾回收信息(由于我当时没有执行 这里就不放截图了)

如果内存一直被占用没有被回收 大概率就是有对象或者数据一直被占用无法执行回收(这里是我们排查问题的关键)

2.3 借助工具分析

由于生产环境不方便排查问题,所以这里借助VisualVM工具

通过如下命令导出快照通过 VisualVM 进行分析

jmap -dump:format=b,file=/tmp/sdc.hprof pid

在这里插入图片描述

VisualVM 工具就不多介绍了,在jdk的bin目录中就可以启动

将我们从服务器上导出的 .hprof 文件导入到VisualVM中进行分析
导入 – > 选择文件
在这里插入图片描述

找到Object类页面

发现char[] 占用极高
在这里插入图片描述

点开发现里面存着大量的文本信息,这些信息正是客户端通过参数传过来的数据,这很不正常,一般来说客户端传递的参数不属于成员变量或者对象,数据会随着线程或者方法的执行完毕而销毁
在这里插入图片描述

感觉问题应该是出现在这里,我们在点开一条数据查看
ps:一般堆内存溢出这个异常都是由于对象重复创建或者数组存储数据太大导致的,而我这次查看没有发现有重复创建对象或者大对象,总之,一开始一头雾水

发现确实有对象在引用着他,所以无法被回收
在这里插入图片描述

2.4 本地复现问题

为了验证并且对比问题,我在本地复现了下生产环境的异常

为了快速模拟堆内存溢出,我将我本地程序堆内存故意调小,借助idea工具将堆内存调整为 60 M
在这里插入图片描述

第二是借助jmeter工具进行压测,由于我的接口是websocket接口,所以还需要借助一个插件
JMeterWebSocketSamplers-1.2.9.jar (大家可以在网上搜一下,如果不能下载可以找我获取, 公主号回复: jw)
将插件放到jmeter的安装目录 /lib/ext/ 下

jmeter的试用这里就不过多介绍了,不熟悉的可以自行查阅一下

我这里设置了 1 个线程,间隔 3 s,一直循环
在这里插入图片描述

通过我们的VisualVM进行监控
在这里插入图片描述

压测后观察服务和VisualVM还有压测工具

通过察看结果树发现 17 次之后就异常了
在这里插入图片描述
服务异常截图:
在这里插入图片描述

观察压测工具:
可以看到50Mb的时候就溢出了(这里大家可能要留意下,后面分析应该还会用到)
在这里插入图片描述

点击Heap Dump 生成快照文件
发现和生产环境是同样的问题
在这里插入图片描述

3. 问题解决过程

接下来就开始排查为啥前端传进来的数据会被一直占用
ps:一开始我为了省事使用http接口压测了一下传输大数据,眼看就把cpu干冒烟了 也没压出异常来 😭😭

那肯定就是websocket的锅 !!!

3.1 websocket缓冲区分析

上面查看分析快照的时候 也发现了被websocket的类HeapCharBuffer引用
在这里插入图片描述

那 HeapCharBuffer 中 messageBufferText 到底是干啥的呢?

messageBufferText 通常是指用于存储接收到的文本消息的缓冲区。当通过 WebSocket 接受到文本消息时,这些消息会暂时存储在这个缓冲区中,直到被应用程序读取。

说到 messageBufferText 那就不得不提到 maxTextMessageBufferSize

maxTextMessageBufferSize 定义了上述缓冲区的最大容量,即可以存储的最大文本消息量或大小。这个值设定了接收缓冲区的界限,防止由于过多未处理的消息导致内存耗尽

这里需要注意⚠️一点,也是引起我们问题的所在:

就是每次建立一个websocket连接都会创建一个最大的缓冲区 maxTextMessageBufferSize

那 maxTextMessageBufferSize 的大小是如何定义的呢,通过websocket的配置类来定义
我之前的定义是 512 k

@Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        // 在此处设置bufferSize
        container.setMaxTextMessageBufferSize(512000);
        container.setMaxBinaryMessageBufferSize(512000);
        container.setMaxSessionIdleTimeout(60 * 1000L); // 60s 超时时间
        return container;
    }

通过上面的分析,可以得出以下结论:

  1. 缓冲区设置的内存偏大导致建立大量连接的时候占用大量内存,而实际每次建立连接的时候缓冲区可能用不了512k
  2. 连接结束之后缓冲区没有被清空

还记得我们上面压测的数据吗,17次之后就异常了,按照我们的结论每次建立连接消耗512k内存,那17次就是8704k 大约8.5mb,我们的堆初始的使用内存大约为40mb多,然后出现溢出的时候使用内存是大约50mb,那正好就是加上我们的8.5mb出现的异常,这里只是一个估算,肯定还有其他地方有内存消耗

为了验证我又将 maxTextMessageBufferSize 的大小调整为了 20k
然后压测 发现300多次才出现异常,那肯定就是因为maxTextMessageBufferSize引起的

但是调整maxTextMessageBufferSize的大小还不能从根本上解决问题,还要找到问题的根源,那就是为啥连接关闭后缓冲区没有被回收。

3.2 GC Root 引用链分析

堆内存都满了,但是数据都没有被垃圾回收只能说明一个问题,就是你的对象还被别人引用着。

接下来我们就通过查看messageBufferText的引用链来寻找问题的根源

我直接截图吧,我们找到有问题的数据(这个在上面使用工具的时候已经讲过了),一开始我们没有往深了挖这个链路,现在通过GC Root这个工具将链路展开

可以很清晰的看到我们的messageBufferText 的大概引用流程:
messageBufferText --> ConcurrentHashMap --> ClassLoader(这个是根级)
messageBufferText一直在被ConcurrentHashMap引用着 无法被释放

在这里插入图片描述

最后我排查了下我的代码,连接每次都会正常关闭,问题就出在 ConcurrentHashMap 上,我使用 ConcurrentHashMap 存储了客户端实例,在存储的时候使用 seesionId 作为key,然后加了前缀。。

大概是这样:
在这里插入图片描述

但是在清除client实例的时候,大概是这样的(当时估计犯神经了):
在这里插入图片描述

神龙见首不见尾 !!!

虽然是由一个不起眼的小问题引起的,但是整个堆内存溢出的排查思路还是值得我们复盘和学习的。

One more thing
原来时间真的不是一条横跨在你面前的河,有着此岸和彼岸,而是一条挂在悬崖上的瀑布,奔流直下,一去无回。

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

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

相关文章

NHANES指标推荐:MDS

文章题目:The association between magnesium depletion score (MDS) and overactive bladder (OAB) among the U.S. population DOI:10.1186/s41043-025-00846-x 中文标题:美国人群镁耗竭评分 (MDS) 与膀胱过度活动症…

【HTML5学习笔记1】html标签(上)

web标准(重点) w3c 构成:结构、表现、行为,结构样式行为相分离 结构:网页元素整理分类 html 表现:外观css 行为:交互 javascript html标签 1.html语法规范 1) 所有标签都在…

计算机视觉---目标检测(Object Detecting)概览

一、目标检测定义与核心任务 1. 定义 任务:在图像/视频中定位并分类所有感兴趣目标,输出边界框(Bounding Box)和类别标签。核心输出: 坐标:((x_1, y_1, x_2, y_2))(左上角右下角)或…

在vue3中使用Cesium的保姆教程

1. 软件下载与安装 1. node安装 Vue.js 的开发依赖于 Node.js 环境,因此我们首先需要安装 Node.js。Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它允许你在服务器端运行 JavaScript 代码,同时也为前端开发提供了强大的工具支…

IP地址、端口、TCP介绍、socket介绍、程序中socket管理

1、IP地址:IP 地址就是 标识网络中设备的一个地址,好比现实生活中的家庭地址。IP 地址的作用是 标识网络中唯一的一台设备的,也就是说通过IP地址能够找到网络中某台设备。 2、端口:代表不同的进程,如下图: 3、socket:…

搭建运行若依微服务版本ruoyi-cloud最新教程

搭建运行若依微服务版本ruoyi-cloud 一、环境准备 JDK > 1.8MySQL > 5.7Maven > 3.0Node > 12Redis > 3 二、后端 2.1数据库准备 在navicat上创建数据库ry-seata、ry-config、ry-cloud运行SQL文件ry_20250425.sql、ry_config_20250224.sql、ry_seata_2021012…

RK3568-鸿蒙5.1与原生固件-扇区对比分析

编译生成的固件目录地址 ../openharmony/out/rk3568/packages/phone/images鸿蒙OS RK3568固件分析 通过查看提供的信息,分析RK3568开发板固件的各个组件及其用途: 主要固件组件 根据终端输出的文件列表,RK3568固件包含以下关键组件&#x…

常见激活函数——作用、意义、特点及实现

文章目录 激活函数的意义常见激活函数及其特点1. Sigmoid(Logistic 函数、S型函数)2. Tanh(双曲正切函数)3. ReLU(Rectified Linear Unit修正线性单元)4. Softmax5. Swish(Google 提出&#xff…

基于微信小程序的在线聊天功能实现:WebSocket通信实战

基于微信小程序的在线聊天功能实现:WebSocket通信实战 摘要 本文将详细介绍如何使用微信小程序结合WebSocket协议开发一个实时在线聊天功能。通过完整的代码示例和分步解析,涵盖界面布局、WebSocket连接管理、消息交互逻辑及服务端实现,适合…

小波变换+注意力机制成为nature收割机

小波变换作为一种新兴的信号分析工具,能够高效地提取信号的局部特征,为复杂数据的处理提供了有力支持。然而,它在捕捉数据中最为关键的部分时仍存在局限性。为了弥补这一不足,我们引入了注意力机制,借助其能够强化关注…

【无标题】威灏光电哲讯科技MES项目启动会圆满举行

5月14日,威灏光电与哲讯科技MES项目启动会在威灏光电总部隆重举行。威灏光电董事长江轮、总经理刘明星、哲讯科技总经理崔新华、副总王子文及双方项目组成员共同出席,标志着两家企业在数字化领域的第二次深度合作正式启航。 强强联手,二度合作…

display:grid网格布局属性说明

网格父级 &#xff1a;display:grid&#xff08;块级网格&#xff09;/ inline-grid&#xff08;行内网格&#xff09; 注意&#xff1a;当设置网格布局&#xff0c;column、float、clear、vertical-align的属性是无效的。 HTML: <ul class"ls02 f18 mt50 sysmt30&…

排序算法之高效排序:快速排序,归并排序,堆排序详解

排序算法之高效排序&#xff1a;快速排序、归并排序、堆排序详解 前言一、快速排序&#xff08;Quick Sort&#xff09;1.1 算法原理1.2 代码实现&#xff08;Python&#xff09;1.3 性能分析 二、归并排序&#xff08;Merge Sort&#xff09;2.1 算法原理2.2 代码实现&#xf…

Java 并发编程归纳总结(可重入锁 | JMM | synchronized 实现原理)

1、锁的可重入 一个不可重入的锁&#xff0c;抢占该锁的方法递归调用自己&#xff0c;或者两个持有该锁的方法之间发生调用&#xff0c;都会发生死锁。以之前实现的显式独占锁为例&#xff0c;在递归调用时会发生死锁&#xff1a; public class MyLock implements Lock {/* 仅…

基于对抗性后训练的快速文本到音频生成:stable-audio-open-small 模型论文速读

Fast Text-to-Audio Generation with Adversarial Post-Training 论文解析 一、引言与背景 文本到音频系统的局限性&#xff1a;当前文本到音频生成系统性能虽佳&#xff0c;但推理速度慢&#xff08;需数秒至数分钟&#xff09;&#xff0c;限制了其在创意领域的应用。 研究…

ADC深入——SNR、SFDR、ENOB等概念

目录 SNR&#xff08;Spurious‑Free Dynamic Range 信噪比&#xff09; ENOB&#xff08;Effective Number Of Bits 有效位&#xff09; SFDR&#xff08;Spurious‑Free Dynamic Range&#xff09; 感觉SNR和SFDR差不多&#xff1f;看看下图 输入带宽 混叠 带通采样/欠…

硬件厂商的MIB文档详解 | 如何查询OID? | MIB Browser实战指南-优雅草卓伊凡

硬件厂商的MIB文档详解 | 如何查询OID? | MIB Browser实战指南-优雅草卓伊凡 一、硬件厂商的MIB文档是什么&#xff1f; 1. MIB的本质&#xff1a;设备的”数据字典” MIB&#xff08;Management Information Base&#xff09; 是SNMP协议的核心数据库&#xff0c;定义了设备…

阿里开源通义万相 Wan2.1-VACE,开启视频创作新时代

0.前言 阿里巴巴于2025年5月14日正式开源了其最新的AI视频生成与编辑模型——通义万相Wan2.1-VACE。这一模型是业界功能最全面的视频生成与编辑工具&#xff0c;能够同时支持多种视频生成和编辑任务&#xff0c;包括文生视频、图像参考视频生成、视频重绘、局部编辑、背景延展…

小学数学题批量生成及检查工具

软件介绍 今天给大家介绍一款近期发现的小工具&#xff0c;它非常实用。 软件特点与出题功能 这款软件体积小巧&#xff0c;不足两兆&#xff0c;具备强大的功能&#xff0c;能够轻松实现批量出题。使用时&#xff0c;只需打开软件&#xff0c;输入最大数和最小数&#xff0c…

5.13/14 linux安装centos及一些操作命令随记

一、环境准备 VMware Workstation版本选择建议 CentOS 7 ISO镜像下载指引 虚拟机硬件配置建议&#xff08;内存/处理器/磁盘空间&#xff09; 二、系统基础命令 一、环境准备 1.VMware Workstation版本选择建议 版本选择依据 选择VMware Workstation的版本时&#xff0c…