【程序大侠传】应用内存缓步攀升,告警如影随形
前序
在武侠编码的江湖中,内存泄漏犹如隐秘杀手,潜伏于应用程序的各个角落,悄无声息地吞噬着系统资源。若不及时发现和解决,必将导致内存枯竭,应用崩溃。
背景:内存泄漏的由来
内存泄漏,乃程序运行过程中,已不再使用的内存块未被及时回收,导致内存使用量不断增加的现象。此问题多发于对象生命周期管理不当之处,如持有对象引用过长,或未能及时释放资源,终致内存枯竭,系统崩溃。
在JVM的世界中,内存泄漏常见于以下几种情况:
- 静态集合类:如 HashMap、ArrayList 等,若不断向其添加对象而不清理,易造成内存泄漏。
- 长生命周期对象持有短生命周期对象引用:如单例模式中的对象持有临时对象引用,导致临时对象无法被垃圾回收。
- 未关闭的资源:如数据库连接、文件流等,若未及时关闭,亦会导致内存泄漏。
- 监听器与回调函数:未及时移除的监听器或回调函数,可能导致对象无法被回收。
解决方案:内存泄漏的破解之道
-
善用工具,探查隐患
如同侠客需借助兵器,程序员亦需运用内存分析工具,如 jvisualvm、jmap、jhat 等,探查内存使用情况,定位内存泄漏之源。常规步骤如下:- 使用 jmap 生成堆转储文件:jmap -dump:live,format=b,file=heap_dump.hprof
- 使用 jvisualvm 或 Eclipse MAT 分析堆转储文件,查找无法回收的对象。
-
优化代码,清理内存
针对发现的内存泄漏问题,需优化代码,确保对象在不再使用时尽快释放。具体方法如下:- 及时清理集合:对于使用完毕的对象,及时从集合中移除。
- 合理管理对象引用:避免长生命周期对象持有短生命周期对象引用,可使用弱引用(WeakReference)来管理。
- 关闭资源:对于文件流、数据库连接等资源,在使用完毕后,务必调用 close() 方法关闭。
- 移除监听器:在适当时机,移除不再需要的监听器或回调函数。
加强监控,防患未然
如同江湖侠客需时刻警惕,程序员亦需持续监控内存使用情况,防患于未然。可使用监控工具如 Prometheus、Grafana 等,实时查看内存使用情况,及时发现异常。
滴滴滴、滴滴滴…,代码剑宗中的某一处洞府中的告警声不绝于耳,近眼望去,洞府正中央的蒲团上正坐着一位双眼紧闭身材健硕的男子,此男子的旁边还摆放着篮球、杠铃等道具,从洞府的摆布不难看出此男子平日经常锻炼,以至于他的身型较于常人更加挺拔高大。男子听到告警声睁开双眼,男子双眸中充满精光,看来此次闭关男子有了不少收获。听到警告声的男子眼神中闪过了一丝不耐烦,嘴里轻轻碎了一声,然后不紧不慢地从袖中拿出一个圆盘,此圆盘此时一直闪烁着红光,并且一直发出“滴滴滴”的声音,男子用手轻抚手中圆盘,身前映射出一个巨大光影,光影里面有一些画面跟文字,男子大概花了一刻钟时间扫描完光影里的内容。他脸上闪过一丝苦涩,然后说道:“该死的,竟然出现了内存告警”,随即只见男子双手一挥把光影打散,男子站了起来朝着旁边的一个房间走去。
而洞府中的男子就是咱们的男主“阿强”。他之前正坐在蒲团上修炼内功到一个关键时刻,不曾想被圆盘的告警打断,此时的阿强心情不是很美丽…
内存紧急处理
阿强离开洞府的第一件事情就是通过告警身份牌进入“乾坤内存法阵”查看告警的应用阵脚,阿强查看应用的内存情况跟系统的一些指标如下图所示,阿强看到这个内存水位情况就发现了不对劲。应用从晚上03:00开始到目前为止内存一直处于一个缓慢上升状态。不过此时阿强倒也没有因此慌了自己阵脚,阿强根据以前处理类似问题的经验,他先通过“乾坤内存法阵”中的应用内存Dump导出功能先将内存快照给dump下来,然后就将应用的容器进行重启的操作。
实时区间热点图
实时线程数
实时gc数量
实时堆内存信息
容器实时内存情况
不久,应用的快照文件就dump了下来,阿强看着dump下来的文件并没有直接去分析而是优先去询问了负责此应用的人询问了一下具体情况。2个时辰后,阿强大概从负责此应用的人口中知道了此应用的基本情况。此应用名叫G服务,是从F服务中拆出来的一个应用,拆出来的G服务的代码内容与G服务是保持一致的,但是G服务的内存表现很稳定,并没有F服务表现出来的内存缓慢爬升的情况,而G服务表现内存缓慢爬升则是随着不断提高流量灰度的一同上升的。其中F服务的一个容器内存情况大致是8台内存16G的云服务器,G服务的容器内存情况是4台8G的云服务器。还有一个值得注意的一个点则是G服务的调用链路由于处于流量切换的过程与在F服务中不同,其区别如:
其中橙色的线表示G服务从F服务拆分出来后多一次交互,也就是说,在流量切换灰度期间,G服务的流量入口是从F服务通过rpc接口方式接受的。
此时的阿强大概了解了G服务应用的基本情况,接下来要做的事情则是去分析内存缓慢爬升的问题,只见他拿出了一法器,此法器名叫“乾坤内存镜”,此法器的作用就是能够清晰地分析应用内存快照文文件,在使用法器有一个细节问题需要注意的是,如果通过此法器直接去打开内存快照文件,此法器会默认进行fullgc,fullgc后的快照文件如下图:
fullgc后的快照文件内存大小明显和实际占用不同,如果想让法器打开快照文件不尽兴fullgc,则只需要换一种打开方式,打开方式如下:
此时你应该能看到如下图的内容说明此次打开方式没有尽兴fullgc,我们只需要稍等片刻即可
通过这种方式打开的快照文件则是如下所示:
阿强看着解析出来的快照内容,此时展现出来内容是通过内存的实例的数量来进行的排序,其中,char[]占用了1412m大小的内存,粗略看下来没有什么大对象。如果是几年前的阿强,他会傻不拉几地去查看char[]实例的引用,但是此时的阿强已经不是两年前的阿强,经过时长两年半的练习,他踩过数不清的坑,经验告诉他,此时你应该看看第三个实例,阿强此时查看第三个实例的Merged outgoing references,他看到此实例的引用
然后再进一步跟进String的引用,除了spring的常规引用,发现logback和jackson有引用大量的字符实例。
阿强此时通过idea打开了G服务的代码,开始查看起来jackson和logback的代码使用点,2个时辰后,阿强发现了一些奇怪的日志打印,如下:
log.info("业务接口处理请求参数明文,{}, http request method:{}",JSON.toJSONString(request.getParams()), methodMapping.getMethod());
上面这种日志打印会将整个请求的入参都打印出来,而且一次请求类似这种打印全部请求入参的日日志大概有5~7次,而由于G服务的承载的业务请求报文都是比较大的,也就是说,每一次请求过来,这种大日子的打印会打印好几次,而这些大而全的日志大部分内容是没用的,并且每次打印生成的字符串每次都是不同的,也就是每次请求在堆内存中生成6~7个大字符对象,这种大字符对象会在堆中频繁创建,会造成youngc很频繁。而youngc过于频繁会造成很多大字符对象进入老年代,导致整个堆内存不断上升。为了验证自己的猜想,阿强尝试着删除G服务中这些大日志的打印,最终发现内存上升的情况有一定的改善(此时的内存已经不会出现缓慢爬坡的情况),但是内存表现相比较F服务还是没有那么好的,因此阿强又只能进一步去分析内存块照文件,2刻钟后,阿强在线程ThreadLocal中发现很多大char[]数组的引用,而这些ThreadLocal都是由rpc线程所持有。
而rpc底层的序列化正是使用的jackson,而com.fasterxml.jackson.core.util.BufferRecycler 是 Jackson 库中的一个工具类,用于高效地管理和重用缓冲区。在多线程环境中,特别是使用 ThreadLocal 时,确实有可能导致内存泄漏:
- ThreadLocal 的生命周期问题:ThreadLocal 变量会与线程的生命周期绑定,如果线程不被回收,ThreadLocal 变量及其引用的对象也不会被回收,可能导致内存泄漏。
- 缓冲区的大小和数量:如果缓冲区的大小或数量非常大,且这些缓冲区长期占用内存而不被释放,可能导致内存使用过多,从而引发内存泄漏。
- 线程池使用不当:在使用线程池时,如果没有正确管理线程池的生命周期和资源,可能会导致线程无法被回收,进而导致 ThreadLocal 引用的对象无法被回收。
到这里真相大白,而阿强面对涉及基础设施的改造,他有点烦躁。凡是涉及基础设施的改动,任务的难度和解决时间就会成倍增加,因为基础设施的改造流程会拉的比较长。但这个任务是一个紧急的任务,为了快速地将问题处理,那怎么能够不去改造基础设施并解决这个问题呢,阿强脑子在飞速的运转,不多时,阿强心中闪过一丝光亮,他紧皱的眉间也开始舒坦。刚刚的那一丝光亮就是快速解决任务的关键,那就是:“类加载器的双亲委派机制!!”
相关文章:

【程序大侠传】应用内存缓步攀升,告警如影随形
前序 在武侠编码的江湖中,内存泄漏犹如隐秘杀手,潜伏于应用程序的各个角落,悄无声息地吞噬着系统资源。若不及时发现和解决,必将导致内存枯竭,应用崩溃。 背景:内存泄漏的由来 内存泄漏,乃程序…...

JavaWEB概述
JavaWEB概述 一、什么是JavaWEB 用Java技术解决web互联网领域的技术栈。要学习JavaWEB首先得知道什么是客户端和服务端 客户端:简而言之,这就是使用方,比如我们下载一个软件去使用,里面有很多我们可以使用的功能,那…...

半结构化知识抽取案例
半结构化知识抽取是指从半结构化数据源(如HTML、XML、JSON等)中提取有用的信息,并将其转换为更易于理解和使用的知识形式。半结构化数据通常包含一些结构化的标记或标签,但不像完全结构化的数据那样严格。 比如抽取如下网页到neo …...

Oracle Truncate和delete的区别
DropTruncatedelete语句类型 DDl (数据定义语言 Data Definition Language DDl (数据定义语言 Data Definition Language DML(数据操作语言 Data Manipulation Language 速度 快 删除整个表 快 一次性删除 慢 逐行删除 回滚不可不可可del…...

应用层协议 --- HTTP
序言 在上一篇文章中,我们在应用层实现了一个非常简单的自定义协议,我们在我们报文的首部添加了报文的长度并且使用特定的符号分割。但是想做一个成熟,完善的协议是不简单的,今天我们就一起看看我们每天都会用到的 HTTP协议 。 UR…...

网卡Network Interface Card
文章目录 网卡(Network Interface Card,简称NIC)是一种计算机硬件设备,用于将计算机连接到计算机网络,使计算机能够进行数据通信。它是计算机与外部网络(如局域网、互联网)之间的接口࿰…...

9.1 Linux_I/O_基本知识
文件类型 一切I/O皆文件,文件就是存放在磁盘上面的有序数据的集合。 文件类型: 常规文件 r :就是普通文件目录文件 d :就是目录,是一个索引字符设备文件 c :键盘、鼠标块设备文件 b :U盘、磁…...

[Java]一、面向对象核心编程思想
G:\Java\1.JavaSE 1. 继承 1.1 继承的概述 重点内容:1.知道继承的好处2.会使用继承3.知道继承之后成员变量以及成员方法的访问特点4.会方法的重写,以及知道方法重写的使用场景5.会使用this关键字调用当前对象中的成员6.会使用super关键字调用父类中的成员7.会定义抽象方法以…...

科研绘图系列:R语言多个AUC曲线图(multiple AUC curves)
文章目录 介绍加载R包导入数据数据预处理画图输出结果组图系统信息介绍 多个ROC曲线在同一张图上可以直观地展示和比较不同模型或方法的性能。这种图通常被称为ROC曲线图,它通过比较不同模型的ROC曲线下的面积(AUC)大小来比较模型的优劣。AUC值越大,模型的诊断或预测效果越…...

JavaWeb--纯小白笔记06:使用Idea创建Web项目,Servlet生命周期,注解,中文乱码解决
使用Idea创建一个web项目----详细步骤配置,传送门:http://t.csdnimg.cn/RsOs7 src:放class文件 web:放html文件 out:运行过后产生的文件 一创建一个新的web项目(配置好了后): 在src创建一个文件…...

jQuery——jQuery的2把利器
1、jQuery 核心函数 ① 简称:jQuery 函数,即为 $ 或者 jQuery ② jQuery 库向外直接暴露的是 $ 或者 jQuery ③ 引入 jQuery 库后,直接使用 $ 即可 当函数用:$(xxx) 当对象用:$.xxx&#x…...

Day29笔记-Python操作pdfPython发送邮件
一、Python操作PDF【了解】 1.pdf 简介 PDF是Portable Document Format的缩写,这类文件通常使用.pdf作为其扩展名。在日常开发工作中,最容易遇到的就是从PDF中读取文本内容以及用已有的内容生成PDF文档这两个任务。 在Python中,可以使用名为P…...

Seata分布式事务实践
理论篇 什么是事务 关于事务我们一定会想到下面这四大特性: 原子性:所有操作要么全都完成,要么全都失败。 一致性: 保证数据库中的完整性约束和声明性约束。 隔离性:对统一资源的操作不会同时发生的。 持久性:对事务完成的操作最终会持久化到数据库中。 理解&…...

数字IC设计\FPGA 职位经典笔试面试整理--基础篇2
1. 卡诺图 逻辑函数表达式可以使用其最小项相加来表示,用所有的最小项可以转换为卡诺图进行逻辑项化简 卡诺图讲解资料1 卡诺图讲解资料2 卡诺图讲解资料3 最小项的定义 一个函数的某个乘积项包含了函数的全部变量,其中每个变量都以原变量或反变量的形…...

(务必收藏)推荐市面上8款AI自动写文献综述的网站
在当前的学术研究和论文写作中,AI技术的应用已经变得越来越普遍。特别是在文献综述这一环节,AI工具能够显著提高效率并减少人工劳动。以下是市面上8款推荐的AI自动写文献综述的网站: 一、千笔-AIPassPaper 是一款备受好评的AI论文写作平台&…...

【python】运算符
学习目标 了解 Python 中常见 算术(数学)运算符赋值运算符 算术(数学)运算符 a 是 10,b 是 20 运算符描述实例加两个对象相加 a b 输出结果 30-减得到负数或是一个数减去另一个数 a - b 输出结果 -10*乘两个数相…...

C++深入学习string类成员函数(1):默认与迭代
引言 在 C 编程中,std::string 类是处理字符串的核心工具之一。作为一个动态管理字符数组的类,它不仅提供了丰富的功能,还通过高效的内存管理和操作接口,极大地方便了字符串操作。通过深入探讨 std::string 的各类成员函数&#…...

DataGrip远程连接Hive
学会用datagrip远程操作hive 连接前提条件: 注意:mysql是否是开启状态 启动hadoop集群 start-all.sh 1、启动hiveserver2服务 nohup hiveserver2 >> /usr/local/soft/hive-3.1.3/hiveserver2.log 2>&1 & 2、beeline连接 beelin…...

go 读取excel
一、安装依赖 go get github.com/tealeg/xlsx二、main.go package mainimport "fmt" import "github.com/tealeg/xlsx"type Student struct {Name stringSex string }func (student Student) show() {fmt.Printf("Name:%s Sex:%s\r\n", stude…...

Linux进阶系列(四)——awk、sed、端口管理、crontab
目录 1. 写在前面2. awk —— 强大的文本处理工具2.1 awk 概述2.2 awk 脚本结构2.3 awk 的内置变量2.4 awk 的高级用法2.5 awk实践 3. sed —— 流式文本编辑器3.1 sed 的基本语法3.2 sed 常用命令3.3 sed 的高级用法 4. Linux 端口管理4.1 端口的概念4.2 查看端口状态4.3 开放…...

利用Metasploit进行信息收集与扫描
Metasploit之信息收集和扫描 在本文中,我们将学习以下内容 使用Metasploit被动收集信息 使用Metasploit主动收集信息 使用Nmap进行端口扫描 使用db_nmap方式进行端口扫描 使用ARP进行主机发现 UDP服务探测 SMB扫描和枚举 SSH版本扫描 FTP扫描 SMTP枚举 …...

基于Pytorch框架的深度学习MODNet网络精细人像分割系统源码
第一步:准备数据 人像精细分割数据,可分割出头发丝,为PPM-100开源数据 第二步:搭建模型 MODNet网络结构如图所示,主要包含3个部分:semantic estimation(S分支)、detail prediction…...

Go语言中的并发编程
Go语言中的并发编程Go语言中的并发编程主要依赖于两个核心概念:goroutine 和 channel。1. Goroutinegoroutine 的特点结束 goroutine2. Channel创建 Channel发送和接收数据Channel 的类型使用 select 语句简单的多个 goroutine使用 WaitGroup 等待所有 goroutine 完…...

python学习笔记(3)——控制语句
控制语句 我们在前面学习的过程中,都是很短的示例代码,没有进行复杂的操作。现在,我们将开始学习流程控制语句。 前面学习的变量、数据类型(整数、浮点数、布尔)、序列(字符串、列表、元组、字 典、集合&am…...

关系数据库设计之Armstrong公理详解
~犬📰余~ “我欲贱而贵,愚而智,贫而富,可乎? 曰:其唯学乎” 一、Armstrong公理简介 Armstrong公理是一组在关系数据库理论中用于推导属性依赖的基本规则。这些公理是以著名计算机科学家威廉阿姆斯特朗&…...

【Geoserver使用】SRS处理选项
文章目录 前言一、Geoserver的三种SRS处理二、对Bounding Boxes计算的影响总结 前言 今天来看看Geoserver中发布图层时的坐标参考处理这一项。根据Geoserver官方文档,坐标参考系统 (CRS) 定义了地理参考空间数据与地球表面实际位置的关系。CRS 是更通用的模型&…...

python里面的单引号和双引号的区别
在Python中,单引号(‘’)和双引号(“”)在大多数情况下是等价的,没有本质区别。它们都用于创建字符串。以下是一些关键点: 功能相同: 两者都可以用来定义字符串,例如&…...

为什么不要在循环,条件或嵌套函数中调用hooks
为什么不要在循环,条件或嵌套函数中调用hooks 前言useState Hook 的工作原理具体实现1、初始化2、第一次渲染3、后续渲染4、事件处理简单代码实现 为什么顺序很重要Bad Component 第一次渲染Bad Component 第二次渲染 总结 前言 自从 React 推出 hooks 的 API 后&a…...

将成功请求的数据 放入apipost接口测试工具,发送给后端后,部分符号丢失
将成功请求的数据 放入apipost接口测试工具,发送给后端后,部分符号丢失 apipost、接口测试、符号、丢失、错乱、变成空格背景 做CA对接,保存CA系统的校验数据,需要模仿前端请求调起接口,以便测试功能完整性。 问题描…...

N诺计算机考研-错题
B A.LLC,逻辑链路控制子层。一个主机中可能有多个进程在运行,它们可能同时与其他的一些进程(在同一主机或多个主机中)进行通信。因此在一个主机的 LLC子层的一个服务访问点,以便向多个进程提供服务。B.MAC地址,称为物理地址、硬件地址,也称为局域网地址,用来定义网络设…...