深入JVM:线上内存泄漏问题诊断与处理

文章目录

  • 深入JVM:线上内存泄漏问题诊断与处理
    • 一、序言
    • 二、内存泄漏概念
    • 三、内存泄漏环境模拟
    • 四、内存泄漏诊断与解决
      • 1、步骤一:获取堆内存快照文件
        • (1)获取正在运行程序dump文件
        • (2)获取已终止程序dump文件
      • 2、步骤二:诊断堆内存快照文件
        • (1)MAT内存分析工具
      • 3、步骤三:定位内存泄漏问题
        • (1)Leak suspects内存泄漏报告
        • (2)Dominator Tree支配树
          • 2.1 支配树原理
        • (3)Histogram直方图
      • 4、步骤四:解决内存泄漏问题
    • 五、后记


深入JVM:线上内存泄漏问题诊断与处理

一、序言

对于Java工程师而言,深入理解JVM(Java虚拟机)不仅是掌握Java程序运行机制的基础,也是提升系统性能、优化应用和解决复杂问题能力的重要一步,更是Java进阶之路的重中之重。

本文小豪将贴近实战,带大家定位并处理内存泄漏问题,同时本文也将介绍目前较为流行的MAT内存分析工具的基本用法,以及其采用的支配树原理,相信阅读过本文之后,大家能够更深入地理解内存泄漏的检测方法和解决策略。

二、内存泄漏概念

在学习本文之前,建议先回顾上一篇【深入JVM:全面解析GC调优】,本文使用到的VisualVM监控工具,在上一篇中有做简要介绍。

在日常开发中,内存泄漏也是一个很常见的问题,首先我们需要明确一个概念,即内存泄漏和内存溢出是两种不同的内存管理问题,虽然它们最终都会导致堆内存的OOM,但它们并不对等。

  1. 内存泄漏:内存泄漏指的是某些对象已经不需要再使用,但其还在GC Root引用链上,垃圾回收器无法识别并回收这些对象,导致这其占用的内存无法被释放,可用的内存逐渐减少,最终导致OOM。内存泄漏基本都是代码逻辑上有问题,造成对象一直被错误地引用。
  2. 内存溢出:内存溢出指的是JVM没有足够的内存空间满足对象的分配,导致内存溢出有可能是程序设计的问题,或者JVM参数配置的堆内存过小了,没有足够的内存空间。

这里我们借助VisualVM监控工具,观察一下堆内存正常使用曲线和异常使用曲线变化情况:

  • 正常情况:堆内存使用呈锯齿形状,在执行垃圾回收之后,内存使用量会下降到一个较为平衡的位置,多次垃圾回收后下降的值接近,一般这种情况,代表程序内存使用正常

在这里插入图片描述

  • 内存泄漏情况:堆内存使用呈逐步递增趋势,在执行垃圾回收之后,内存使用量的位置越来越高,直到使用满为止,这种情况就代表程序存在内存泄漏问题

在这里插入图片描述

三、内存泄漏环境模拟

这里我们模拟一个内存泄漏现象,提供对外接口,每次都将新创建的对象放置于static静态变量集合中,始终保持引用:

public class FullGcController {public static Map<String, byte[]> map;// 简略内存泄漏static {map = new HashMap<>();}@GetMapping("/addMemory")public void addMemory() {// 随机IDString autoId = UUID.randomUUID().toString();// 创建对象,占用的内存大小为10Mbyte[] memory = new byte[1024 * 1024 * 2];map.put(autoId, memory);}}

然后使用Postman测试工具不断调用接口,直到产生OOM

java.lang.OutOfMemoryError: Java heap space

四、内存泄漏诊断与解决

在我们定位并解决内存泄漏问题之前,首先应该是先发生程序出现了内存泄漏,当然这部分工作更多是由系统运维、测试人员去完成的,比较专业一点的运维可能会采用目标比较流行的Prometheus + Grafana工具监控线上运行的程序,定期向我们反馈。

而我们开发调试中,也可用利用之前提到的VisualVM监控工具,在线观察内存使用变化。

但在绝大多数情况下,并不一定会有专门的运维人员监控线上程序,等出现内存泄漏问题时,往往是客户发现软件打不开了,我们跟踪到落盘日志时,才发现日志中打印出OutOfMemoryError异常。

在这种情况下,就要求我们导出堆内存快照dump文件,使用专业的内存分析工具定位内存泄漏:

1、步骤一:获取堆内存快照文件

首先第一步就是获取到堆内存快照dump文件,这里分为两种情况,第一种是程序仍在运行,第二种是程序已经终止。

(1)获取正在运行程序dump文件

如果程序正在运行,获取dump文件的方法还是比较多的。

  • VisualVM:我们可用通过上面提到的VisualVM工具,点击[堆 Dump]快速导出dump文件:

在这里插入图片描述

VisualVM会返回生成的dump文件路径所在位置:

在这里插入图片描述

  • Jmap命令:jmap是JDK自带的命令行工具,用于生成JVM堆内存快照,具体用法也比较简单:
// 文件名以.hprof为后缀,pid为Java进程号
jmap -dump:format=b,file=文件名.hprof [pid]
(2)获取已终止程序dump文件

第二种情况即程序已经挂掉了,这就要求程序运行前在JVM启动命令中添加自动生成dump文件的命令,这里需要添加两个命令:

// 当堆抛出OOM异常时,导出当前的堆内存快照
-XX:+HeapDumpOnOutOfMemoryError// 指定生成堆内存快照的路径
-XX:HeapDumpPath=<路径>

当程序发生OOM时,会在我们配置的指定路径下自动生成堆内存快照dump文件。

2、步骤二:诊断堆内存快照文件

在导出堆内存快照dump文件后,需要借助一些专业的内存分析工具帮我们智能诊断内存问题。

(1)MAT内存分析工具

MAT(Memory Analyzer Tool)是一款快速便捷且功能强大丰富的JVM堆内存离线分析工具,它是Eclipse开发工具的一个插件,一般我们独立下载MAT即可【官网在这】。

这里需要注意对应JDK与MAT的版本,小豪这里使用的是JDK 8,对应MAT的1.11版本:

在这里插入图片描述

同时由于堆内存快照dump文件可能比较大,这里需要在MAT配置文件中调整其启动内存大小,一般建议调整为dump文件的1.5倍:

在这里插入图片描述

接着启动MAT,选择File -> Open Heap Dump导入dump文件,默认选择Leak suspects Report,智能生成详细的内存泄漏报告。

3、步骤三:定位内存泄漏问题

(1)Leak suspects内存泄漏报告

生成后的内存泄漏报告如下:

在这里插入图片描述

针对我们模拟的内存泄漏问题,到这里其实MAT工具已经分析出来了,报告里显示FullGcController类中的一个HashMap实例对象占用了99.27%的内存,通过这些信息我们很容易就能定位到代码块,当然这个例子比较简单,实际业务中可能会较为复杂。

小豪在这里也补充MAT其它两个常用功能:Dominator TreeHistogram

(2)Dominator Tree支配树

支配树是另一种比较重要的功能,MAT中支配树代表对象之间的支配关系,它不同于Java中GC Root的引用链

2.1 支配树原理

支配树是一种图形表示,在一张有向图中,确定一个起始点,如果起始点到终止点B的每条路径都经过A,那么称AB支配点(如下图,经过对象D的路径都经过对象A,则对象A支配对象D)。

在这里插入图片描述

通过支配树,MAT快速识别出哪些对象占用了大量的内存,这里有两个概念:

  • Shallow Heap(浅堆):代表对象自身占用的内存
  • Retained Heap(深堆):代表对象自身和其关联的对象占用的内存

MAT内存泄漏检测的原理其实主要就是依据支配树,若对象的深堆大小超过一定比例,则怀疑其为造成内存泄漏的对象

在这里插入图片描述

比如我们的FullGcController对象自身只占用了8字节,但其支配的对象占用了大量内存,基本就可以断定,问题出在FullGcController对象中。

这里继续选择FullGcController对象,右键点击[List objects],根据引用关系继续往下追,定位它具体引用了哪些大对象:

在这里插入图片描述

  • with outgoing references:其引用的对象
  • with incoming references:其被哪些对象引用

进一步定位到它里面占用内存最大的对象为map

在这里插入图片描述

(3)Histogram直方图

直方图主要展示所有类实例的大小:

在这里插入图片描述

大致用法与支配树类似,根据Retained Heap深堆由大到小排列,分析具体占用内存较大的对象是谁。

4、步骤四:解决内存泄漏问题

至此我们已经定位到了代码中产生内存泄漏的对象了,剩下的就是优化设计修改代码。

小豪之前处理的一个线上内存泄漏问题的场景是这样的:当时我们的服务作为一个数据中台,接收其它厂商推过来的视频流地址,我们去解析推送过来的数据包。结果有个小伙伴先将接收到的数据包写入一个静态的阻塞队列Queue,另外开启了一个线程监听此阻塞队列,但在从阻塞队列取出数据包消费后却没有将其删除,导致阻塞队列越积越多,果不其然最终OOM了。

本文主要介绍的是内存泄漏问题的定位与解决方案,其实定位内存溢出的问题也同理,不过产生内存溢出的原因可能更为复杂一下,常见的有:

  1. 并发请求量过高,业务处理时占用大量内存
  2. 大文件报表导出等,一次性加载过多数据
  3. 堆内存空间分配过小

这些具体问题就要具体分析了,比如并发请求量过高可以引入中间件异步处理,进行限流保护,大文件报表可以选择分批导出,减少内存开销,当然在优化设计之后也要进行完整的测试验证。

五、后记

本文从内存泄漏的概念开始介绍,通过环境模拟,逐步带大家学习MAT内存分析工具定位并诊断内存泄漏的过程,同时额外引申出支配树的原理。

最后我们总结一下针对内存泄漏的处理流程:

  1. 获取到堆内存快照dump文件(关键)
  2. 借助内存分析工具(MAT等),导入dump文件智能诊断内存泄漏
  3. 定位到内存泄漏源头后,优化代码设计或调整技术方案

如果大家觉得内容有价值,不妨考虑点点赞,关注关注小豪,后续小豪将会继续更新JVM相关系列文章,大家共同进步~

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

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

相关文章

FCN-语义分割中的全卷积网络

FCN-语义分割中的全卷积网络 语义分割 语义分割是计算机视觉中的关键任务之一&#xff0c;现实中&#xff0c;越来越多的应用场景需要从影像中推理出相关的知识或语义&#xff08;即由具体到抽象的过程&#xff09;。作为计算机视觉的核心问题&#xff0c;语义分割对于场景理…

软件游戏提示msvcp120.dll丢失的解决方法,总结多种靠谱的解决方法

在电脑使用过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“找不到msvcp120.dll”。那么&#xff0c;msvcp120.dll是什么&#xff1f;它对电脑有什么影响&#xff1f;有哪些解决方法&#xff1f;本文将从以下几个方面进行探讨。 一&#xff0c;了解msv…

htb-linux-1-lame-smb3.0.20

namp smb漏洞 搜索关键词 其他 smb Samba是在linux和unix系统上实现SMB&#xff08;Server Message Block&#xff0c;信息服务块&#xff09;协议的一款免费软件。SMB是一种在局域网上共享文件和打印机的通信协议&#xff0c;它在局域网内使用linux和Windows系统的机器之…

手把手带你做一个自己的网络调试助手(2) - TCP服务器完善

服务器指定客户端发送 自定义控件comboBox - 刷新客户端列表 目的&#xff1a; 自定义控件&#xff0c;当鼠标点击这个comboBox控件的时候去刷新客户端列表 mycombobox.h #ifndef MYCOMBOBOX_H #define MYCOMBOBOX_H#include <QComboBox> #include <QWidget>cl…

运维实用小脚本,登录即自动显示系统信息

今天给大家安利一个超级实用的Linux小技巧&#xff0c;让你每次登录终端时都能感受到满满的科技感和效率爆棚&#xff01; 你是否厌倦了每次手动检查系统状态&#xff0c;像内存使用、CPU负载这些繁琐操作&#xff1f;别担心&#xff0c;一个小调整&#xff0c;让这一切自动化…

技术与业务的完美融合:大数据BI如何真正提升业务价值

数据分析有一点经典案例 沃尔玛的啤酒和尿布案例 开始做BI的时候&#xff0c;大家肯定都看过书&#xff0c;那么一定也看过一个经典的案例&#xff0c;就是沃尔玛的啤酒和尿布的案例。这个案例确实很经典&#xff0c;但其实是一个失败的案例。为什么这么说呢&#xff1f;很明显…

Android.mk文件生成的so工程文件并Debug调试native code

1.这里主要展示一下从最原始先新建一个工程 2.将hello的子工程文件放入上面新建好的工程里面&#xff0c;直接拷贝放置这里 3.修改根目录下的settings.gradle 加入hello 4.app工程下的build.gradle加入依赖&#xff0c;这样就可以识别hello中的java包文件 5.MainActivity 中来&…

RabbitMQ-topic exchange使用方法

RabbitMQ-默认读、写方式介绍 RabbitMQ-发布/订阅模式 RabbitMQ-直连交换机(direct)使用方法 目录 1、概述 2、topic交换机使用方法 2.1 适用场景 2.2 解决方案 3、代码实现 3.1 源代码实现 3.2 运行记录 4、小结 1、概述 topic 交换机是比直连交换机功能更加强大的…

信息学奥赛初赛天天练-24-二叉树、N叉树遍历技巧与前缀表达式、中缀表达式、后缀表达式应用实战演练

PDF文档公众号回复关键字:20240609 单项选择题(共15题,每题2分,共计30分:每题有且仅有一个正确选项) 5 根节点的高度为1,一根拥有2023个节点的三叉树高度至少为( )。 A 6 B 7 C 8 D 9 8 后缀表达式 6 2 3 + - 3 8 2 / + * 2 ^ 3 + 对应的中缀表达式是( ) A ((…

「动态规划」打家劫舍的变形题,你会做吗?

213. 打家劫舍 IIhttps://leetcode.cn/problems/house-robber-ii/description/ 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈&#xff0c;这意味着第一个房屋和最后一个房屋是紧挨着的。同时&#x…

使用 Vue 官方脚手架初始化 Vue3 项目

Vite 官网&#xff1a;https://cn.vitejs.dev/ Vue 官网&#xff1a;https://vuejs.org/ Vue 官方文档&#xff1a;https://cn.vuejs.org/guide/introduction.html Element Plus 官网&#xff1a;https://element-plus.org/ Tailwind CSS 官网&#xff1a;https://tailwindcss.…

Codeforces Round 951 (Div. 2)C. Earning on Bets

Problem - C - Codeforces 合理的答案&#xff1a; 求出 k1 ~ kn 的最小公倍数lcm&#xff0c;如果 lcm/k1 lcm/k2 ... lcm/kn < lcm 即符合题意。 左边之和为我们付的总钱数&#xff0c;右边才是每次选择得到的钱数(都为lcm)。 直接拿1e9检查是否可以分即可&#xff…

基于Django+MySQL的智慧校园系统

此项目基于Django MySQL HTML CSS JS jQuery bootstrap实现的功能有 学生管理部门管理代办清单管理校园论坛校园医疗服务校园看点校园生活助手常用功能入口 1. 一些注意点 1. 页面body会自动有一些边界距&#xff0c;处理方法&#xff1a; <head><style>b…

你还在纠结U盘怎么选吗?小白带你来看

前言 2024年的618活动已经开始了&#xff0c;这个活动买电子产品着实是比其他时间要便宜很多。 前几天小白的一个好朋友问我&#xff1a;U盘该怎么选&#xff1f; 呃&#xff0c;本来是想写“老朋友”的&#xff0c;结果她愣是要我改成“好朋友”。 行吧&#xff0c;那就好朋…

车载电子电气架构 --- 车载信息安全

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

Vite - 开发初体验,以及按需导入配置

目录 开始 创建一个 Vite 项目 项目结构 /src/main.js index.html package.json vite.config.js Vite 项目中使用 vue-router Vite 组件的“按需引入” 传统的方式引入一个组件 传统方式引入带来的问题 解决办法&#xff08;配置 按需引入 插件&#xff09; 示例&…

OpenFeign远程接口调用使用公共模块出现的错误

今天在使用openfeign和sentinel实现fallback服务降级时遇到找不到类型的异常 检查代码发现没有错误&#xff0c;EnableFeignClients也在启动类上标注了 错误信息&#xff1a;A component required a bean of type com.zxc.cloud.apis.PayFeignSentinelApi that could not be f…

《精通ChatGPT:从入门到大师的Prompt指南》第9章:实战练习

第9章&#xff1a;实战练习 9.1 Prompt练习题 在本节中&#xff0c;我们将提供一系列练习题&#xff0c;旨在帮助读者通过实际操作提升使用ChatGPT的能力。这些练习题涵盖了从基础到高级的不同难度级别&#xff0c;并针对各种应用场景设计&#xff0c;确保读者能够在实际使用…

基于Springboot + vue实现的火锅店管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

私有云和多云管理平台 | Cloudpods v3.11.4 正式发布

本次 3.11.4 更新亮点为&#xff1a;系统镜像引入社区镜像&#xff0c;用户可以一键导入各主流开源操作系统镜像&#xff0c;方便用户上手使用。持续迭代共享 LVM&#xff0c;支持快照&#xff0c;主备机等特性&#xff0c;修复迁移删除镜像缓存等 BUG。 功能优化 【费用】费…