【工作中问题解决实践 十】一次内存泄露排查-MAT使用指南

最近体验了一把当医生的感觉,定位病根病因,感觉这种要揪出问题的感觉很爽,并不觉得麻烦,这里将整个排查过程记录一下,方便之后再遇到类似问题有应对之道。

问题背景

2023-07-18 早上还在睡梦中的俺被一条条报警消息铛铛铛的吵醒,这才发现我们的服务早上突然大量请求499,由于我们的服务相对基础,所以马上故障组拉群开始排查解决,先感觉分批次重启容器止损。这里get到的两个重点:

  • 监控一定要做,有了监控心里就有底了,且阈值设置的稍微低一些,这样可以赶在实际影响业务前得到通知
  • 遇到问题一定要及时摘流重启止损,问题分析现场留存先放放,当然如果监控阈值低的情况下,可以先摘流dump一下内存

我们当然是采取了及时止损的方式,当然也就丢失了现场,所以排查问题需要降低监控阈值再等一周发现后处理,不过这个没关系,首先不影响线上稳定才是第一要务!

实际原因

首先499的含义是客户端主动关闭连接,那是为什么呢?是QPS扛不住吗?并没有,实际上当天监控显示QPS才160多,远低于压测值。继而又观察容器本身的监控状态,这才发现容器内存已经飚到94%了,而且不止一台,所有容器都在94%左右,所以并非是单台容器的问题。于是我们把目光转向了JVM:
在这里插入图片描述
这才惊讶的发现,老年代一直不回收,堆内存匀速上升,一直仰赖每周两次的发版上线让其恢复起点,18日这个周二早上的上一个发版日发版时间过早,就这么点儿时间差,堆内存直接飚了上来。于是乎我们大概知道了问题原因:堆内存一定发生了泄露

内存泄露

Java内存泄露是指在Java应用程序中,由于未正确释放不再使用的内存,导致内存占用不断增加,最终可能耗尽可用内存资源,导致应用程序性能下降甚至崩溃。内存泄露是一种常见的程序缺陷,可能由多种原因引起,以下是一些可能导致内存泄露的原因:

  1. 对象引用未被释放:如果在使用完一个对象后未显式将其引用置为null,该对象可能无法被垃圾收集器回收,从而导致内存泄露。

  2. 静态引用:静态变量持有对象的引用,如果没有正确管理静态引用,即使对象不再使用,也无法被垃圾收集器回收。

  3. 集合类未正确管理:集合类(如List、Map、Set)可能会持有对象的引用,如果在集合中添加了对象但未在后续操作中正确移除,这些对象可能会一直保持在内存中。

  4. 资源未释放:如果程序使用了文件、网络连接、数据库连接等资源,但未正确关闭这些资源,可能会导致资源占用不释放,从而引发内存泄露。

  5. 监听器未移除:如果在应用中注册了监听器(例如GUI事件监听器、定时任务等),但忘记在不需要时移除这些监听器,可能导致对象无法被回收。

  6. 循环引用:对象之间相互引用,形成循环引用时,即使对象本身不再被使用,由于彼此之间的引用关系,也无法被垃圾收集器回收。

  7. 使用缓存:虽然缓存可以提高性能,但如果未正确管理缓存的生命周期和大小,可能导致缓存中的对象一直保持在内存中,引发内存泄露。

  8. 异常处理不当:异常可能导致程序流程中断,如果异常处理不当,可能导致资源未正确释放。

要避免内存泄露,开发人员应该仔细管理对象的生命周期,确保及时释放不再需要的资源和引用。使用强引用、软引用、弱引用等不同类型的引用,可以帮助更好地控制对象的回收时机。同时,定期进行代码审查和性能分析,以及使用内存分析工具来检测和解决潜在的内存泄露问题也是非常重要的。

我们常用的Spring框架也有一些常见内存泄露常见,在Spring框架中,由于其底层使用了Java语言,内存泄露问题也可能出现。以下是一些常见导致内存泄露的Spring框架相关行为:

  1. 未关闭资源: Spring管理了许多资源,如数据库连接、会话、事务等。如果在使用完这些资源后未正确关闭,可能导致资源未释放,最终引发内存泄露。

  2. Spring Bean 生命周期管理不当: 如果在配置Spring Bean时没有正确设置作用域或生命周期,可能导致Bean无法正确地被销毁,从而引发内存泄露。

  3. 内部类持有外部类的引用: Spring中的一些组件,如事件监听器、AOP代理等,可能会导致内部类持有外部类的引用,从而阻止外部类的垃圾回收,导致内存泄露。

  4. 使用缓存不当: Spring提供了缓存支持,但如果在使用缓存时没有正确管理缓存的生命周期和大小,可能导致缓存中的对象一直保持在内存中,引发内存泄露。

  5. 定时任务未正确管理: Spring的定时任务功能可能导致内存泄露,如果任务未正确取消或管理,可能导致对象无法被垃圾收集器回收。

  6. 循环依赖: Spring容器中的循环依赖问题可能导致Bean无法正确销毁,从而引发内存泄露。

  7. 使用ApplicationContext引起的泄露: 如果在应用程序中持有了ApplicationContext的引用,而没有正确释放,可能会导致整个ApplicationContext及其管理的Bean无法被回收。

为了避免在Spring应用程序中出现内存泄露,开发人员应该注意上述问题,并采取适当的措施来管理资源、Bean的生命周期,以及其他Spring相关的组件。进行定期的代码审查和性能分析,使用内存分析工具,可以帮助发现和解决潜在的内存泄露问题。

标红重点!因为我们这次内存泄露就与此相关:继续提问:使用ApplicationContext引起的泄露举个例子

  • 使用ApplicationContext引起的泄露是指在应用程序中持有了ApplicationContext的引用,但没有正确释放,从而导致整个ApplicationContext及其管理的Bean无法被垃圾回收,进而导致内存泄露。以下是一个例子来说明这种情况:假设你有一个Spring Boot应用程序,其中包含一个自定义的ApplicationContextHolder类,用于持有ApplicationContext的引用:
public class ApplicationContextHolder {
    private static ApplicationContext applicationContext;

    public static void setApplicationContext(ApplicationContext context) {
        applicationContext = context;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
}

在应用程序启动时,你将ApplicationContext设置到ApplicationContextHolder中:

@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
        ApplicationContext context = SpringApplication.run(MyApp.class, args);
        ApplicationContextHolder.setApplicationContext(context);
    }
}

在某个地方,你可能会通过ApplicationContextHolder来获取ApplicationContext,例如在一个服务类中:

public class MyService {
    public void doSomething() {
        ApplicationContext context = ApplicationContextHolder.getApplicationContext();
        // 使用ApplicationContext执行一些操作
        // ...
    }
}

这种做法可能导致内存泄漏,因为ApplicationContext被持有并可能无法被垃圾收集器回收。例如,如果MyService的实例一直存在,它持有的ApplicationContext引用也会一直存在,从而阻止ApplicationContext及其管理的Bean被释放

要避免这种情况,应该尽量避免在应用程序中持有ApplicationContext的引用。ApplicationContext通常由Spring框架管理,它会在合适的时候进行销毁和垃圾回收。如果确实需要在某些地方获取ApplicationContext,应该在使用完之后及时释放引用,避免长时间持有引用导致内存泄漏。通常情况下,可以通过依赖注入(DI)等方式来获取所需的Bean,而无需直接持有ApplicationContext的引用。

DUMP内存

dump的方式有很多,很多公司内部集成了自己的工具,但dump时首先要干的是摘流,确保你dump内存时没有请求进来,不要影响业务的正常使用。当然通用的在Java中,可以使用一些内存分析工具和指令来监测和分析应用程序的内存使用情况。以下是一些常见的Java内存分析指令和工具:

  1. jps (Java进程状态工具):用于列出当前系统中运行的Java进程,并显示它们的进程ID和主类名称。

    示例用法:jps -v

  2. jstat (Java统计信息监视工具):用于收集和显示Java虚拟机(JVM)运行时的各种统计信息,如堆内存使用情况、垃圾回收统计等。

    示例用法:jstat -gc <pid> <interval> <count>

  3. jmap (Java内存映像工具):用于生成堆转储快照,可以用于分析堆内存使用情况、对象分布等。

    示例用法:jmap -dump:live,format=b,file=<filename> <pid>

  4. jhat (Java堆分析工具):用于分析jmap生成的堆转储快照,可以通过浏览器查看对象信息和引用关系。

    示例用法:jhat <heap_dump_file>

  5. jstack (Java堆栈跟踪工具):用于生成Java线程的堆栈跟踪信息,帮助识别死锁、线程等待等问题。

    示例用法:jstack <pid>

  6. VisualVM (Visual Java Monitoring and Management Console):一个图形化工具,可以用于监视和分析应用程序的性能和内存使用情况,提供了多种功能,包括堆转储、线程分析等。

  7. MAT (Eclipse Memory Analyzer):一个强大的内存分析工具,用于分析堆转储快照,帮助识别内存泄漏和优化内存使用。

这些工具和指令可以帮助开发人员诊断应用程序的内存问题,包括内存泄漏、垃圾回收性能等方面的情况。根据具体的问题和需求,你可以选择合适的工具和指令来进行内存分析。

MAT分析

在这里下载最新版本的MAT :Eclipse Memory Analyzer
在这里插入图片描述
因为最新版的MAT仅支持JDK17及以上,所以在这里:JDK20下载下载最新版的JDK
在这里插入图片描述
还需要注意的是MAT需要进行配置,如果你电脑安装了多个JDK版本需要指定,并且如果你要分析的DUMP文件比较大,需要调大MAT的配置:这里我的配置参考如下:

-vm 
C:\Program Files\Java\jdk-20\bin\javaw.exe
-startup
plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.700.v20221108-1024
-vmargs
--add-exports=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
-Xmx8192m

内存分配了8个G,然后指定了JDK运行版本,这样就可以了。

寻根定位

好的,现在dump文件也有了,MAT工具也就位了【附送网上找的MAT最全介绍一文深度讲解JVM 内存分析工具 MAT及实践】,那就开始着手分析,MAT会自动给出探测意见:
在这里插入图片描述
同时会告诉你三个内存泄露可能导致的实际原因
在这里插入图片描述
甚至会贴心的告诉你问题之间可能存在关联:
在这里插入图片描述
最厉害的是点击问题进入,它甚至能给你直接打印出堆栈!告诉你哪里的代码发生泄露了
在这里插入图片描述

从堆栈入口很容易就定位问题了。问题代码不便于贴出来,实际原因就是这里没有使用Spring的容器管理,而是通过通过AutowireCapableBeanFactory容器外注入的方式注入bean的。而applicationContext被获取用来管理这些bean,且applicationContext被FormFactory引用着。而某个大佬又在applicationContext里疯狂实例对象,三天左右大概1100万个对象。这个不泄露都说不过去

总结一下

照例总结一下,线上出了问题不要慌,也别想着保留现场,先止损!平时的报警机制要建立好且阈值要低些,这样才能先于业务发现并解决问题。还有就是MAT是真香!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring事务管理

1.什么是事务 数据库事务是指作为单个逻辑工作单元执行的一系列操作&#xff0c;这些操作要么一起成功&#xff0c;要么一起失败&#xff0c;是一个不可分割的工作单元。 涉及到事务的场景非常多&#xff0c;一个 service 中往往需要调用不同的 dao 层方法&#xff0c;这些方法…

区块链技术助力慈善,为您的善举赋予全新力量!

我们怀揣着一颗温暖的心&#xff0c;秉承着公开透明的理念&#xff0c;带着信任与责任&#xff0c;倾力打造了一套区块链技术驱动的去中心化捐赠与物资分发系统&#xff0c;通过智能生态网络&#xff08;IEN&#xff09;解决捐赠不透明问题的系统&#xff0c;让您的善举直接温暖…

Linux命令200例:cd用于改变当前工作目录(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

【Rust】Rust学习 第五章使用结构体组织相关联的数据

5.1 定义结构体并实例化结构体 定义结构体&#xff0c;需要使用 struct 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。接着&#xff0c;在大括号中&#xff0c;定义每一部分数据的名字和类型&#xff0c;我们称为 字段&#xff08;field&…

数据结构--最小生成树

数据结构–最小生成树 连通图 \color{red}连通图 连通图的生成树是 包含图中全部顶点的一个极小连通子图 \color{red}包含图中全部顶点的一个极小连通子图 包含图中全部顶点的一个极小连通子图。 若图中顶点数为n&#xff0c;则它的生成树含有 n-1 条边。对生成树而言&#xff…

断路器回路电阻试验

试验目的 断路器回路电阻主要取决于断路器动、 静触头的接触电阻, 其大小直接影响正常 运行时的发热情况及切断短路电流的性能, 是反应安装检修质量的重要数据。 试验设备 回路电阻测试仪 厂家&#xff1a; 湖北众拓高试代销 试验接线 对于单断口的断路器, 通过断口两端的接线…

WebRTC 之音视频同步

在网络视频会议中&#xff0c; 我们常会遇到音视频不同步的问题&#xff0c; 我们有一个专有名词 lip-sync 唇同步来描述这类问题&#xff0c;当我们看到人的嘴唇动作与听到的声音对不上的时候&#xff0c;不同步的问题就出现了 而在线会议中&#xff0c; 听见清晰的声音是优先…

redis 集群 2:分而治之 —— Codis

在大数据高并发场景下&#xff0c;单个 Redis 实例往往会显得捉襟见肘。首先体现在内存上&#xff0c;单个 Redis 的内存不宜过大&#xff0c;内存太大会导致 rdb 文件过大&#xff0c;进一步导致主从同步时全量同步时间过长&#xff0c;在实例重启恢复时也会消耗很长的数据加载…

Mysql主从搭建 基于DOCKER

创建目录 #主节点目录 mkdir -p /home/data/master/mysql/#从节点目录 mkdir -p /home/data/slave/mysql/创建配置文件 # 主节点配置 touch /home/data/master/mysql/my.cnf# 从节点配置 touch /home/data/slave/mysql/my.cnf编辑配置文件 主节点配置文件 vim /home/data/m…

前沿分享-鱼形机器人

可能并不太前沿了&#xff0c;是21年底的新闻了&#xff0c;但是看见了就顺便发一下吧。 大概就是&#xff0c;通过在pH响应型水凝胶中编码不同的膨胀速率而构建了一种环境适应型变形微机器人,让微型机器人直接向癌细胞输送药物从而减轻药物带来副作用。 技术原理是&#xff0c…

【51单片机】晨启科技,7针OLED显示驱动程序,STC89C52RC

文章目录 原理图oled.coled.hmain.c 原理图 sbit OLED_SCLP4^3;//SCL-D0 sbit OLED_SDAP4^1;//SDA-D1 sbit OLED_RES P3^6;//RES sbit OLED_DC P3^7;//DC sbit OLED_CSP2^7; //CS oled.c #include "OLED.h"//******************************说明*******************…

APP外包开发的Flutter框架

Flutter 是一种流行的开源UI框架&#xff0c;由谷歌开发&#xff0c;用于构建跨平台的移动应用程序。它使用一套统一的代码库&#xff0c;可以在多个平台上&#xff08;如Android、iOS、Web、桌面等&#xff09;保持一致的外观和行为。今天和大家分享一些基于 Flutter 开发的常…

初次使用GPU云服务器

前言&#xff1a; 在体验了GPU云服务器&#xff08;GPU Cloud Computing&#xff0c;GPU&#xff09;后&#xff0c;我认为这是一个非常强大的弹性计算服务。它为深度学习、科学计算、图形可视化、视频处理等多种应用场景提供了强大的GPU算力&#xff0c;能够满足各类用户的计算…

如何使Python Docker镜像安全、快速、小巧

一、说明 在微服务领域&#xff0c;拥有安全、高效和紧凑的 Docker 映像对于成功部署至关重要。本博客将探讨有助于构建此类映像的关键因素&#xff0c;包括不以 root 用户身份运行映像的重要性、在构建映像时更新和升级包、在编写 Dockerfile 指令时考虑 Docker 的层架构&…

ZIG:理解未来编程语言的视角

文章目录 摘要&#xff1a;引言&#xff1a;性能简洁性和模块化避免常见错误和陷阱总结&#xff1a;参考资料&#x1f4d1;: 摘要&#xff1a; 本文介绍了新兴编程语言ZIG的目标和特点&#xff0c;包括高性能、简洁性和模块化&#xff0c;并分析了这些特点是如何通过语言设计来…

关于丢失安卓秘钥的撞sha-1值的办法

实验得知&#xff0c;安卓sha-1和keytool生成秘钥签名文件的时间有关。 前提条件是&#xff0c;开发者必须知道生成秘钥的所有细节参数 以下是撞文件代码&#xff08;重复生成&#xff09; import time import osidx 0while True:cmdkeytool -keyalg RSA -genkeypair -alia…

中国信通院腾讯安全发布《2023数据安全治理与实践白皮书》

导读 腾讯科技(深圳)有限公司和中国信息通信研究院云计算与大数据研究所共同编制了本报告。本报告提出了覆盖组织保障、管理流程、技术体系的以风险为核心的数据安全治理体系&#xff0c;并选取了云场景、互娱、社交等场景&#xff0c;介绍相应场景下数据安全治理实践路线及主…

26 MFC序列化函数

文章目录 Serialize对于存储文件的序列化 Serialize Serialize 是一个在 MFC (Microsoft Foundation Classes) 中常用的函数或概念。它用于将对象的数据进行序列化和反序列化&#xff0c;便于在不同的场景中保存、传输和恢复对象的状态。 在 MFC 中&#xff0c;Serialize 函数…

MongoDB 入门

1.1 数据库管理系统 在了解MongoDB之前需要先了解先数据库管理系统 1.1.1 什么是数据&#xff1f; 数据&#xff08;英语&#xff1a;data&#xff09;&#xff0c;是指未经过处理的原始记录。 一般而言&#xff0c;数据缺乏组织及分类&#xff0c;无法明确的表达事物代表的意…

elk开启组件监控

elk开启组件监控 效果&#xff1a; logstash配置 /etc/logstash/logstash.yml rootnode1:~# grep -Ev "^#|^$" /etc/logstash/logstash.yml path.data: /var/lib/logstash path.logs: /var/log/logstash xpack.monitoring.enabled: true xpack.monitoring.elasti…