深入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() {

        // 随机ID
        String autoId = UUID.randomUUID().toString();
        // 创建对象,占用的内存大小为10M
        byte[] 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相关系列文章,大家共同进步~

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

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

相关文章

HP Laptop 14s-fr1xxx原厂oem预装Win11系统ISO镜像下载

惠普星青春版14s-fr1xxx笔记本电脑原装出厂Windows11系统安装包&#xff0c;恢复出厂开箱状态一模一样 链接&#xff1a;https://pan.baidu.com/s/11Qe5XgCmH3emIVEpvoKclg?pwdm1qe 提取码&#xff1a;m1qe 适用型号&#xff1a;14s-fr1xxx 14s-fr0001AU、14s-fr0002AU、…

VMware Fusion 如何增加linux硬盘空间并成功挂载

文章目录 0. 前言1. 增加硬盘空间2. 硬盘分区2.1 查看硬盘2.2 分区2.3 格式化2.4 挂载 3. 参考 0. 前言 如果发现虚拟机分配的硬盘不足&#xff0c;需要增加硬盘空间。本文教给大家如何增加硬盘空间并成功挂载。 查看当前硬盘使用情况&#xff1a; df -h可以看到&#xff0c…

sqli-labs 靶场 less-7 第七关详解:OUTFILE注入与配置

SQLi-Labs是一个用于学习和练习SQL注入漏洞的开源应用程序。通过它&#xff0c;我们可以学习如何识别和利用不同类型的SQL注入漏洞&#xff0c;并了解如何修复和防范这些漏洞。Less 7 SQLI DUMB SERIES-7判断注入点 进入页面中&#xff0c;并输入数据查看结果。 发现空数据提…

求宇文玥在水下的浮力和赵丽颖捞他的时间

关注微信公众号 数据分析螺丝钉 免费领取价值万元的python/java/商业分析/数据结构与算法学习资料 2024年汉东省在达康书记的带领下率先实现高考试点改革。为让更多的考生能提升对他们的理解和记忆&#xff0c;把电视剧的场景融入考试题目中。确保学生看一遍就懂&#xff0c;想…

debian12安装时分区方案

一、初次尝试 一共设置了4个分区&#xff0c;其中根目录/分区46G&#xff0c;swap分区10G&#xff08;电脑内存为6G&#xff09;&#xff0c;/boot分区200M&#xff0c;/home分区55G。系统安装之后的实际占有情况为&#xff1a; 二、调整后情况 一共设置了4个分区&#xff0c…

基于R语言BIOMOD2 及机器学习方法的物种分布模拟与案例分析

原文链接&#xff1a;基于R语言BIOMOD2 及机器学习方法的物种分布模拟与案例分析https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247606139&idx4&snf94ec30bfb5fa7ac0320403d49db3b66&chksmfa821e9ccdf5978a44a9ba96f6e04a121c0bbf63beea0940b385011c0b…

Spring运维之boo项目表现层测试匹配响应执行状态响应体JSON和响应头

匹配响应执行状态 我们创建了测试环境 而且发送了虚拟的请求 我们接下来要进行验证 验证请求和预期值是否匹配 MVC结果匹配器 匹配上了 匹配失败 package com.example.demo;import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Auto…

【网络教程】Iptables官方教程-学习笔记7-简单理解IPTABLES规则的作用流程

前面学习了IPTABLES的所有功能介绍后&#xff0c;一个Linux设备里的IPTABLES规则集是如何运行的&#xff0c;这里简单做个介绍。 在Linux设备里输入"iptables -nvl",得到该设备的所有防火墙规则&#xff0c;得到的结果中可以看到这个设备防火墙里所有的链以及链里的…

2024年CKA模拟系统制作 | step-by-step | 1、基础环境准备

目录 一、软件环境 二、虚拟网络环境准备 1、编辑虚拟网络 2、网络设置 三、新建虚拟主机 1、新建目录 2、新建虚拟主机 四、系统安装 1、装载系统镜像 2、开启虚拟机 3、选择语言 4、键盘选择 5、网络配置 6、代理设置 7、设置软件源 8、存储设置 9、名称设置 …

计算机网络 —— 网络层 (路由协议)

计算机网络 —— 网络层 &#xff08;路由协议&#xff09; 什么是路由协议内部网关协议RIP关键特性 OSPF主要特点 外部网关协议BGP关键特性 我们今天来看路由协议&#xff1a; 什么是路由协议 路由协议是网络设备&#xff08;主要是路由器&#xff09;用来决定数据包在网络中…

【爬虫实战项目一】Python爬取豆瓣电影榜单数据

目录 一、环境准备 二、编写代码 2.1 分页分析 2.2 编码 一、环境准备 安装requests和lxml pip install requests pip install lxml 二、编写代码 2.1 分页分析 编写代码前我们先看看榜单的url 我们假如要爬取五页的数据&#xff0c;那么五个url分别是&#xff1a; htt…

工业互联网数字中台建设方案(ppt原件)

工业互联网数字中台解决方案旨在为企业提供全面、高效的数据驱动能力。该方案主要包括以下几个核心部分&#xff1a; 数据中台&#xff1a;作为核心&#xff0c;数据中台负责汇聚、整合、提纯和加工各类工业数据&#xff0c;实现数据资产的标准化、模型化和模块化。通过提供API…

cmake使用make和Ninja构建对比

前提 make和Ninja是两个常见的构建工具&#xff0c;在网上查阅了一些资料&#xff0c;说是Ninja比make构建速度要快很多。但是具体不知道快多少&#xff0c;所以趁着这次编译clang的机会&#xff0c;分享下它们在时间方面差多少。 步骤 下载llvm 参考llvm官网&#xff0c;这…

linux系统的网络工具和命令非常多,应该如何学习可以快速提高?

目录 一、linux的网络命令有多少&#xff1f; &#xff08;一&#xff09;网络配置 &#xff08;二&#xff09;网络监控 &#xff08;三&#xff09;网络诊断 &#xff08;四&#xff09;网络通信 &#xff08;五&#xff09;网络文件传输 &#xff08;六&#xff09;网…

java web:springboot mysql开发的一套家政预约上门服务系统源码:家政上门服务系统的运行流程

java web&#xff1a;springboot mysql开发的一套家政预约上门服务系统源码&#xff1a;家政上门服务系统的运行流程 家政上门服务系统的优势 服务质量更稳定&#xff1a;由专业的家政人员提供服务&#xff0c;经过严格的培训和筛选。 价格更透明&#xff1a;采用套餐式收费&…

Valgo,类型安全,表达能⼒强的go验证器

valgo 是一个为 Go 语言设计的类型安全、表达性强且可扩展的验证库。该库的特点包括&#xff1a; github.com/cohesivestack/valgo 类型安全&#xff1a;利用 Go 语言的泛型特性&#xff08;从 Go 1.18 版本开始支持&#xff09;&#xff0c;确保验证逻辑的类型安全。表达性&a…

UFS协议入门-分层结构

写在前面:本文参考UFS jedec3.1,本文思维导图如下 1. 分层概述 UFS协议分为3层,从上至下分别是:应用层(UAP),传输层(UTP),互联层(UIC),具体结构如下图所示。 2.1 应用层 在应用层(UAP)中,包括:UFS指令集(UCS),设备管理器(Device Manager),任务管理器(Task Manager…

线性代数|机器学习-P10最小二乘法的四种方案

文章目录 1. 概述2. SVD奇异值分解3. 最小二乘法方程解4. 最小二乘法图像解释5. Gram-Schmidt 1. 概述 当我们需要根据一堆数据点去拟合出一条近似的直线的时候&#xff0c;就会用到 最小二乘法 .根据矩阵A的情况&#xff0c;有如下四种方法 在r n m 时&#xff0c;SVD奇异…

Java Web学习笔记30——打包部署

打包&#xff1a; 到资源管理器中再看下&#xff1a; 将这些文件压缩成一个zip文件&#xff0c;然后到nginx的html目录中执行unzip 解压即可。 部署&#xff1a; Nginx&#xff1a;Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代…

SprirngBoot+Vue房屋租赁系统(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 角色对应功能 租客管理员 功能截图