JVM内存泄漏问题分析处理实战

一、背景

文章开头,先分享一张大部分Java开发同学都记在心里的一张图。

图片

没错,就是Spring Bean生命周期图。就因为这张图不熟悉,导致线上环境出现内存泄漏问题,系统频繁FullGC,服务无法响应。

1、第一次报错系统监控现象

图片

图片

2、关键时间节点:

14:16 机器发布新代码

15:35 机器开始出现fullGC

15:50 机器fullGC耗时上升

17:48 对JVM进行dump操作,然后进行机器置换

由图可知,在14:16发布完成后,系统正常运行了一段时间,期间内存、线程等均未出现异常,不过当系统运行了一段时间后,通过监控可以明显发现内存使用量和线程数都在持续上升,那这样问题就很明确了:

1.有大量阻塞线程

2.存在内存泄露问题

二、 排查过程

分析线程Dump文件

图片

Dump文件记录

通过截图中Dump文件内容可知,HSFBizProcessor-DEFAULT-9-thread-792 这个线程已经阻塞了116s,并且的阻塞线程共有682个。

三、 问题分析原因

根据线程堆栈信息,查到了线程是阻塞在下面这段代码:

@Componentpublic class OssClient implements BeanPostProcessor {private OSS ossClient = null;/**      * 初始化OSS客户端     **/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 省略代码……// 以下是阻塞代码行        ossClient = new OSSClientBuilder().build(ossProperty.getString("endpoint"),                        ossProperty.getString("accessKeyId"),                        ossProperty.getString("accessKeySecret"),                        configuration);// 省略代码……return bean;    }}

这段代码本意是在应用启动时,通过动态配置文件来配置OSS客户端。

但是线程阻塞在了这行,首先我想到可能是由于OSS客户端初始化需要发起网络请求,因为饿了么有张北和南通机房且一般情况下跨机房无法访问,所以第一时间检查了一下配置,果不其然,南通机房配置了张北的OSS。

登录上南通机房的机器,尝试PING张北的OSS域名,发现无法PING通,验证了我的猜测。

图片

四、 第一次问题解决

Get到了报错原因,就方便解决了;通过修改配置,将OSS机房配置正确后,重启机器即可。

1、第二次报错系统监控现象

本来以为万事大吉,在观察了30分钟,确认系统无BLOCKED线程后,就认为该问题已经解决。

图片

图片

2、关键时间节点:

19:48 机器发布新代码

22:30 机器开始出现fullGC

23:30 机器fullGC耗时上升

00:30 对JVM进行dump操作,然后进行机器置换

然而,在发布后3个小时以后,系统又开始报错,同样是fullGC,只不过这次fullGC耗时没有之前那么长了。

3、分析线程Dump文件

因为有了前车之鉴,所以第一步想到的就是上一步的问题没有解决,线程仍然阻塞在刚才的代码处。

图片

不过,这次并没有查询到阻塞线程。这至少证明:

1.阻塞线程确实是由于OSS跨单元拒绝访问导致的

2.还有其他问题导致了内存泄漏

4、分析GC Dump文件

首先,通过集团Grace工具,发现有严重的内存泄漏问题。

图片

这里显示有11万个org.apache.http.impl.conn.PoolingHttpClientConnectionManager实例,占用了80.42%的堆内存,但是这个类并不是我直接引入的,那么一定是有间接依赖,生成了大量该类对象。

另外,通过类名,能判断这个对象是和网络请求有关系,而我这个应用上需要网络请求的地方有几处:

1.访问DB

2.访问Redis

3.访问OSS

4.进行HSF调用

继续通过对对象依赖进行分析,发现了一个重要信息:

图片

org.apache.http.impl.conn.PoolingHttpClientConnectionManager这个类由OSS间接依赖进来的,确定了引起内存泄漏的罪魁祸首。

虽然定位到了是由于OSS建议依赖进来,但是看代码仍然不能解释为什么会产生内存泄漏。

​​​​​​​

@Componentpublic class OssClient implements BeanPostProcessor {private OSS ossClient = null;/**      * 初始化OSS客户端     **/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 省略代码……// 一下是阻塞代码行        ossClient = new OSSClientBuilder().build(ossProperty.getString("endpoint"),                        ossProperty.getString("accessKeyId"),                        ossProperty.getString("accessKeySecret"),                        configuration);// 省略代码……return bean;    }}

排查原因过程中,有一篇文章给了我答案,下面是这篇文章给的OOM原因的解释:

每次new OSSClient的时候,都会往List中放入HttpClientConnectionManager,但是没有主动调用OSSClient的shutdown的方法,所以List只会增大不会变小。反观我们的代码,每次接口调用都会创建一个OSSClient对象,但却在使用完之后,没有调用OSSClient的shutdown方法,导致未调用IdleConnectionReaper的removeConnectionManager方法,使得IdleConnectionReaper中静态列表存储的PoolingHttpClientConnectionManager实例数据一直会增长,一直都不会被回收,最终带来的结果就是OOM。

其实通过代码能够看出,我的初衷是在OssClient这个Bean初始化的时候执行一下初始化逻辑,在我查到导致内存泄漏的原因后,我仍然对一个问题很是不解:为什么OSS初始化的代码会被多次执行?

回到文章标题和开头,为什么这篇文章标题叫“一次由于八股文引起的内存泄漏”,以及为什么文章开头会引入下面这张图?

图片



实际上,是由于实现错了接口导致的OSS初始化代码被重复调用,最终导致系统OOM。

五、 最终问题解决

改变一下实现接口,使代码逻辑符合我预期效果即可,当然这个解决方式有多种多样,下面只是我的一种解决方案。​​​​​​​

@Componentpublic class OssClient implements InitializingBean {
    private OSS ossClient = null;
    /**      * 初始化OSS客户端     **/    @Override    public void afterPropertiesSet() throws Exception {        // 省略代码……        // 以下是阻塞代码行        ossClient = new OSSClientBuilder().build(ossProperty.getString("endpoint"),                        ossProperty.getString("accessKeyId"),                        ossProperty.getString("accessKeySecret"),                        configuration);        // 省略代码……    }
}

总结

        圈内常有声音抱怨,“面试好比是造火箭,而工作不过是拧螺丝”,尤其对于Java开发岗位面试中的常规知识题目持有轻蔑态度。然而,这些被称作“八股文”的知识,实际上是每位开发工程师技术根基的核心。坚实的基础才能确保构建在其之上的高楼大厦能够屹立不倒,历经岁月的洗礼。

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

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

相关文章

vscode预览github上的markdown效果

需要安装的插件有: Github Markdown Preview Markdown Checkboxes Markdown Emoji Markdown footnotes Markdown Preview Github Styling Markdown Preview Mermaid Support Markdown yaml Preamble ctrlshiftv结合双页功能

澳福实例说明真实交易中止损单和限价单的区别

很多投资者不明白止损单和限价单的区别,今天澳福就举一个例子来说明真实交易中止损单和限价单的区别。 紫色椭圆显示了在欧元兑美元图表上的位置,在不稳定的增长之后,澳福 外汇看到了另一波修正,没有看涨的迹象。同时也发现从历史…

APIfox自动化编排场景(二)

测试流程控制条件 你可以在测试场景中新增流程控制条件(循环、判断、等待、分组)等。进一步满足了更复杂的测试场景/流程配置的使用,最终借助自动化测试功能解决复杂场景的测试工作。 分组​ 当测试流程中多个步骤存在相关联关系时&#xf…

《Java程序设计》实验报告(二)之面向对象编程基础

实验内容及步骤: 编写不带构造函数的类并测试。(学生类、圆类)(1)代码: class Student { String name"张三"; int age20; String sex"男";//gender String getName(){…

Deepin基本环境查看(八)【系统安全:房、车、查房、查车】

Deepin基本环境查看(八)【系统安全:房、车、查房、查车】 - 相关文章目录1、概述2、想象中的... 现实中的...1)想象中的我2)梦幻中的我3)现实中的我 3 要房、要车、还是房车都要1)超级计算机2&a…

做跨境电商为什么需要使用住宅代理IP?

住宅代理IP是近年来跨境电商领域日益受到重视的技术工具,不仅可以保护隐私、优化网络速度,还能助推跨境电商的精细化管理。接下来,我们将深入探讨利用住宅代理IP如何为跨境电商业务带来竞争优势。 一、住宅代理IP与跨境电商 住宅代理IP&…

Android开发--实时监测系统+部署故障诊断算法

0.项目整体思路介绍: 搭建无人装备模拟实验平台,使用采集器对数据进行采集,通过网络通信Udp协议发送到安卓端,安卓端作界面显示,算法使用matlab仿真后,用C语言实现。将采集器采集到的数据经过处理后训练&a…

解决计算机“缺失ffmpeg.dll”报错?修复ffmpeg.dll文件方案

在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是“ffmpeg.dll丢失”。ffmpeg.dll是FFmpeg多媒体框架中的一个重要组件,它负责处理音频和视频的编解码。当打开某些软件时,如果系统找不到该文件,就会出现这…

【Linux取经路】探寻shell的实现原理

文章目录 一、打印命令行提示符二、读取键盘输入的指令三、指令切割四、普通命令的执行五、内建指令执行5.1 cd指令5.2 export指令5.3 echo指令 六、结语 一、打印命令行提示符 const char* getusername() // 获取用户名 {return getenv("USER"); }const char* geth…

Multisim14.0仿真(五十五)汽车转向灯设计

一、功能描述: 左转向:左侧指示灯循环依次闪亮; 右转向:右侧指示灯循环依次闪亮; 刹车: 所有灯常亮; 正常: 所有灯熄灭。 二、主要芯片: 74LS161D 74LS04D 74…

运维必会篇-日志(错误日志,二进制日志,查询日志,慢查询日志)

日志 错误日志 错误日志是 MySQL 中最重要的日志之一,它记录了当 mysqld 启动和停止时,以及服务器在运行过 程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日 志。 该日志是默认开启的&#x…

LINUX基础培训二十四之shell字符串处理

一、shell字符串 字符串(String)就是一系列字符的组合。字符串是 Shell 编程中最常用的数据类型之一(除了数字和字符串,也没有其他类型了)。字符串可以由单引号 包围,也可以由双引号" "包围&…

laravel distinct查询问题,laravel子查询写法

直接调用后,count查询会和实际查询的数据对不上,count还是查询全部数据,而实际的列表是去重的。 给distinct加上参数,比如去重的值的id,就加id。 另一种写法是使用group by id 子查询。 sql语句: selec…

echarts使用之折线图(二)

1.基本使用 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compatible" cont…

CSS综合案例4

CSS综合案例4 1. 综合案例 我们来做一个静态的轮播图。 2. 分析思路 首先需要加载一张背景图进去需要4个小圆点&#xff0c;设置样式&#xff0c;并用定位和平移调整位置添加两个箭头&#xff0c;也是需要用定位和位移进行调整位置 3. 代码演示 html文件 <!DOCTYPE htm…

服务器被黑,安装Linux RootKit木马

前言 疫情还没有结束&#xff0c;放假只能猫家里继续分析和研究最新的攻击技术和样本了&#xff0c;正好前段时间群里有人说服务器被黑&#xff0c;然后扔了个样本在群里&#xff0c;今天咱就拿这个样本开刀&#xff0c;给大家研究一下这个样本究竟是个啥&#xff0c;顺便也给…

mmdetection使用自己的voc数据集训练模型实战

一.自己数据集整理 将labelimg格式数据集进行整理 1.1. 更换图片后缀为jpg import os import shutilroot_path/media/ai-developer/imgfileos.listdir(root_path)for img in file:if img.endswith(jpeg) or img.endswith(JPG) or img.endswith(png):img_pathos.path.join(root…

python实现飞书群机器人消息通知(消息卡片)

python实现飞书群机器人消息通知 直接上代码 """ 飞书群机器人发送通知 """ import time import urllib3 import datetimeurllib3.disable_warnings()class FlybookRobotAlert():def __init__(self):self.webhook webhook_urlself.headers {…

vue electron应用调exe程序

描述 用Python写了一个本地服务编译成exe程序&#xff0c;在electron程序启动后&#xff0c;自动执行exe程序 实现 1. 使用node的child_process模块可以执行windows执行&#xff0c;通过指令调exe程序 // electron/index.js var cp require("child_process"); /…

1080p 显示屏分辨率玩游戏的大有人在

喜欢玩游戏的其实大可不必为不能把自己的主机升级到4060焦虑&#xff0c;也不必望着最新的显卡天梯图眼馋兴叹。根据 Steam 平台的调查&#xff0c;六成 Steam 玩家仍然还在用 1080p 显示屏分辨率玩游戏。 根据Steam硬件调查4月份的榜单&#xff0c;1920x1080分辨率依然占据了6…