您当前的位置:首页 > 电脑百科 > 站长技术 > 服务器

线上内存泄漏!如何破案

时间:2022-09-28 11:47:46  来源:  作者:腾讯技术工程

作者:rangobAI,腾讯CSIG数据工程师

| 导语要有性能意识,量变引起质变,简单如一行日志都会在高并发的情况引发血案,考验着研发的技术功底

一 案件背景

9月的某个上午,业务侧突然反馈线上数据服务响应慢,造成任务积压,正常情况下耗时5ms的服务,单次响应达到了5s量级. 收到反馈后我们马上开始排查服务状况,但发现各项指标很健康,接口平均耗时3ms,p99约为1s,和经验值比无太大差别. 业务侧随后补充反馈是某些请求很慢,感觉是若干pod有问题,当流量打到这几台机器时就会变慢.

开始怀疑是网络问题,但没有证据.随后小库网关的一台机器突然宕机,这个现象引起了我们注意.在上次迭代中,我们服务有一次重大升级,所有请求均会经过网关服务转发,以实现Server/DB单元化绑定,问题可能出在转发环节.

为了验证猜想,我们重启了网关,随后业务侧积压现象迅速消失,排查范围锁定网关服务.

二 调查过程

小库网关本身无太多业务逻辑,依赖项非常少,为了实现时间最小损耗,技术框架采用spring cloud gateway,底层WebFlux 则使用了高性能的 Reactor 模式.在上线前的多次压测中,网关服务表现非常优秀,时间损耗基本在毫秒级别.万万没想到上线没几天就挖了个大坑,必须要刨根问底调查清楚

2.1 第一次定位

由于刚迁移到新私密机房,可用于问题还原的监控手段不多,刚开始只能从查看pod云监控以及日志着手.

首先查看日志异常 :可以看到确实有大量PT5S超时存在,但是根据traceId追踪大多数超时请求在服务侧均为毫秒级响应.

其次查看业务侧请求量变化:虽然我们服务有过qps破万的表现,但是问题时间段请求量异常的平稳,甚至略有降低.

最后查看问题时间段性能表现:cpu在40%左右,无突增现象.Pod内存监控由于JVM提前分配,无太大参考价值.I/O流量在问题时间段没有波动.

磁盘满了

此时没有清晰的调查方向,就在我们黔驴技穷的时候,又一台网关pod重启了,我们同学迅速登录机器,查看案发现场-磁盘爆满, 200G的云硬盘在上线的两天内就被打满,请求block.线上请求量其实大大超出我们的预估,生产日志量接近10G/小时/台, 日志保留7天的策略顿时显得不合时宜.

随后我们清理了所有pod的日志,并采取了两个策略:

  • 日志清理策略设为保留1天,因为有日志本身上报cls,保留太久日志没有必要
  • 清理无效日志,打印要有技巧性,不是越多越好,高并发下对服务伤害性越大.

修改上线后,服务运行平稳,再也没出现上午的问题,调查小组愉快的度过了第一天.

2.2 第二次定位

就在以为我们万事大吉的时候,现实光速打脸,第二天早上8点半,生命线里出现大量异常警告,随后网关服务陆续重启,登录机器,磁盘利用率10%,尴了个尬.

CPU问题三板斧

因为自动重启没有保留案发现场,所以只能在昨天的基础上继续调查.

第一个合理怀疑的方向是CPU,虽然CPU利用率40%不能算很高, 但网关和业务机器比达到了1:2,对于仅转发请求的网关来说仍然是不正常的高了.排查此类问题就要启动CPU问题三板斧了

第一板斧: jps查看JAVA进程ID 46

第二板斧: top -Hp 46 查看进程所有线程的活动

可以看出仍然是log4j2的101线程占用了最多的CPU能力,

最后一板斧: jstack pid 查看线程活动,以定位线程堆栈

这个时候,日志打印占用了最多cpu已经一目了然.

最大利器Arthas

但是作为成熟的研发,我们当然不满足于三板斧调查问题,这时候就要请出线上另一大杀器arthas :profiler | arthas,以下介绍来自官网:

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率Arthas(阿尔萨斯)能为你做什么?这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!是否有一个全局视角来查看系统的运行状况?有什么办法可以监控到 JVM 的实时运行状态?怎么快速定位应用的热点,生成火焰图?怎样直接从 JVM 内查找某个类的实例?

不得不说,阿里在java技术积淀远超我司.因为之前已在镜像里面集成了阿尔萨斯,我们立即启动cpu profiler采样生成火焰图.

果然除了日志打印以外,我们又发现了一处CPU热点

这两个地方合计占掉了60%以上的性能.

CPU高的原因

首先分析一下日志占用过高,这是一个使用log4j2的问题,涉及日志打印参数调优,我们之前已经优化过一轮的参数

#RingBuffer大小 AsyncLogger.RingBufferSize=524288#日志等待策略sleepAsyncLogger.WaitStrategy=SLEEP#Ringbuffer满了后直接丢弃log4j2.AsyncQueueFullPolicy=Discard

理论上这已经是性能最好的日志策略,为什么会出现占用CPU负载的问题?

问题出在LockSupport.parkNanos上,简单来说当日志消费速度赶不上生产速度的时候,日志线程会调用这个方法自旋等待若干纳秒,在线程数少的时候性能影响不明显,但是在高并发的情况下会造成大量线程在短时间内频繁唤醒/等待,从而影响业务性能.解决方案:是把自旋的时间间隔调大,如下

AsyncLogger.RingBufferSize=524288AsyncLogger.WaitStrategy=SLEEPlog4j2.AsyncQueueFullPolicy=DiscardAsyncLogger.SleepTimeNs=500

分析下第二个CPU热点,这个问题没那么复杂

是一个对象深拷贝的性能问题,每次请求来的时候都会将一个大对象先序列化在反序列化,这个在请求量低的时候影响较小,但是在我们每天几千万的请求量冲击下,性能瓶颈非常明显.

讲一下对象拷贝的四种解决方案:

JSON : 非常规,吃CPU

Apache BeanUtils :性能最差,不建议使用

Spring BeanUtils: 性能稍好

MapStruct MapStruct – Java bean mAppings, the easy way!,性能无损,推荐!!

具体各自的拷贝原理不再深入分析,大家可以搜资料查看

热点问题解决了

给一下优化前后的CPU对比,以下优化结果是在请求量翻倍同时pod数减半的CPU表现:

优化前: 45%+

优化后: 11%

2.3 最终定位

随着一步步的分析,我们也越来越接近问题的真相: CPU虽然有点高,但仍不足以解释缓慢和重启的现象,另外问题是在线上请求量增大以及随时间推移逐渐暴漏的,几乎可以断定网关存在内存泄漏.于是我们把调查重点投向JVM内存,布下天罗地网,静等凶手再次犯案.

功夫不负有心人,在部署约一天后,几台服务器又开始重启,我们迅速登录还未重启的机器,执行以下操作

  • 首先查看jvm内存已经逼近100%
  • GC非常活跃且无效,大量的内存无法回收
  • 通过火焰图查看的CPU绝大部分在执行GC
  • jmap -dump:format=b,file=heapdump.phrof pid 生成内存dump并上传cos
内存杀器Jprofile

这个时候就要请出排查内存问题的另一大杀器Jprofile,具体资料可以在网上搜,这里主要介绍定位过程.

首先查看内存分布,有1000W+的ImmutableTag,不是我们的业务对象...非常意外

其次查看大对象,SimpleMeterRegistry占用了80%的内存空间!!!

这两个类均属于io.micrometer的核心组件,用来暴漏服务参数供监控使用.Micrometer中包含的SimpleMeterRegistry,它在内存中维护每个meter的最新值.

再一次分析内存中保留的对象内容

可以看出1000W+的对象中,全部记录的是我们每次请求的RouteUri Method 耗时等信息,通过关键字定位,这些对象生产的源头是GateWay的 GatewayMetricsFilter组件,这里会记录所有路由请求的信息apply到micrometer中.而这个GatewayMetricsFilter的启用条件是存在Spring Boot Actuator组件.我们业务中刚好引用了该组件.

至此凶手归案,我们下线Actuator,并手动将GatewayMetricsFilter启动设置为False后,问题彻底解决.

三 内存泄漏原因

但是为什么呢?一个Spring官方提供的监控组件会导致内存泄漏?为什么对象持续无法回收?直觉告诉我们一定是哪个地方不太对劲.珍贵的食材往往需要最简单的烹饪方式,最复杂的场景往往用最朴素的手段抽丝剥茧

3.1 DEBUG过程

幸好我们有一套完整可用的开发环境,足够做场景复现,打好断点触发请求.经过几轮分析,犯案原因也随之浮出水面.

首先还是从SimpleMeterRegistry的引用链开始,(过程比较无聊,不再赘述)

这里存在一条清晰的引用关系,查看MeterRegistry源码,有一个ConcurrentHashMap全局变量

就是这个全局的Hashmap保存了到ImmutableTag的引用关系,导致GC Roots判定引用路径存在,对象存活无法回收.关键代码:

这里判定如果meterMap中不存在mappedId就创建,mappedId是一个DefaultMeter对象,针对我们的业务场景,这个Meter根据Route对象生成.看一下我们的使用方法,为了做到动态路由效果,我们使用了一个全局的filter拦截请求,然后根据算法确定需要转发的目标IP,每次请求都会生成一个新的Route对象

3.2 水落石出

坏就坏在这个newRoute上,因为每次请求的参数不一样,导致我们生成的Route对象也不一样.我们认为Route是请求级别的动态的,每次请求后自然消亡,实际上也是如此.但是万万没想到,站在SpringCloud GateWay或者说站在GatewayMetricsFilter的视角,这个Route是全局的静态,由此引发内存泄漏.

四 经验总结

第一, 要有性能意识,量变引起质变,简单如一行日志都会在高并发的情况引发一起血案,考验着研发的技术功底.

第二,工欲善其事,必先利其器,一款好的工具能够极大提升研发生产力



Tags:内存泄漏   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
记一次Rust内存泄漏排查之旅
在某次持续压测过程中,我们发现 GreptimeDB 的 Frontend 节点内存即使在请求量平稳的阶段也在持续上涨,直至被 OOM kill。我们判断 Frontend 应该是有内存泄漏了,于是开启了排...【详细内容】
2024-02-27  Search: 内存泄漏  点击:(12)  评论:(0)  加入收藏
在项目中如何避免和解决Java内存泄漏问题
在Java中,内存泄漏通常指的是程序中存在一些不再使用的对象或数据结构仍然保持对内存的引用,从而导致这些对象无法被垃圾回收器回收,最终导致内存占用不断增加,进而影响程序的性...【详细内容】
2024-02-01  Search: 内存泄漏  点击:(70)  评论:(0)  加入收藏
何为内存泄漏?如何监测并防止内存泄漏事故发生
本文关键要点: 1)当应用程序无法返回分配的内存时,就会发生内存泄漏,逐渐消耗更多内存并可能导致系统崩溃。2)用户可以通过监控系统的 RAM 使用情况来识别任何稳定消耗更多内存的...【详细内容】
2023-12-18  Search: 内存泄漏  点击:(71)  评论:(0)  加入收藏
Go 语言中的map和内存泄漏
Map在内存中总是会增长;它不会收缩。因此,如果map导致了一些内存问题,你可以尝试不同的选项,比如强制 Go 重新创建map或使用指针。在 Go 中使用map时,我们需要了解map增长和收缩...【详细内容】
2023-11-23  Search: 内存泄漏  点击:(249)  评论:(0)  加入收藏
Android使用LeakCanary检测内存泄漏
Java四种引用在Java中,有四种不同的引用类型,分别是强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。 强引用(Strong Refere...【详细内容】
2023-11-01  Search: 内存泄漏  点击:(218)  评论:(0)  加入收藏
如何避免Java内存泄漏,来看看这个
引言:在Java应用程序开发中,内存泄漏是一个常见而严重的问题。本文将帮助Java开发人员和软件工程师了解内存泄漏的危害,并提供解决方案。了解内存泄漏: 内存泄漏是指分配的内存...【详细内容】
2023-10-30  Search: 内存泄漏  点击:(242)  评论:(0)  加入收藏
如何避免JavaScript中的内存泄漏?
作者 | 葡萄城技术团队原文链接:https://my.oschina.net/powertoolsteam/blog/10122640前言过去,我们浏览静态网站时无须过多关注内存管理,因为加载新页面时,之前的页面信息会从...【详细内容】
2023-10-27  Search: 内存泄漏  点击:(114)  评论:(0)  加入收藏
大厂面试必问:内存泄漏和内存溢出的区别?
程序的运行需要内存。只要程序提出要求,操作系统或者运行时就必须供给内存。对于持续运行的服务进程,必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则...【详细内容】
2023-05-26  Search: 内存泄漏  点击:(179)  评论:(0)  加入收藏
C/C++中内存泄漏的三种情况分析
内存泄漏是指一块动态分配的内存没有释放,同时又失去了所有对它的引用。在C语言中,这种情况通常发生在使用函数(如malloc或calloc)动态分配内存时,没有使用free函数来释放不再需...【详细内容】
2023-04-13  Search: 内存泄漏  点击:(112)  评论:(0)  加入收藏
深入了解 JavaScript 内存泄漏
作者:京东零售 谢天在任何语言开发的过程中,对于内存的管理都非常重要,Javascript 也不例外。然而在前端浏览器中,用户一般不会在一个页面停留很久,即使有一点内存泄漏,重新加载页...【详细内容】
2023-03-23  Search: 内存泄漏  点击:(149)  评论:(0)  加入收藏
▌简易百科推荐
为什么Nginx被称为“反向”代理呢?
Nginx(发音为"engine-x")是一款高性能、轻量级的开源Web服务器软件,也可用作反向代理服务器、负载均衡器和HTTP缓存。Nginx之所以有被称为“反向”代理,是因为它充当客户端设备...【详细内容】
2024-02-01  coderidea  微信公众号  Tags:Nginx   点击:(60)  评论:(0)  加入收藏
哪种服务器操作系统更好呢?
在当今的IT世界中,服务器操作系统扮演着至关重要的角色。它们是确保服务器能够高效、安全地运行的关键因素。然而,对于许多人来说,服务器操作系统的种类和特点可能是一个复杂的...【详细内容】
2024-01-30    简易百科  Tags:操作系统   点击:(76)  评论:(0)  加入收藏
什么是VPS服务器
VPS服务器是一种虚拟化技术,它将一台物理服务器划分为多个虚拟的独立服务器,每个虚拟服务器都可以拥有自己的操作系统、运行环境、应用程序等。这种技术使得每个虚拟服务器可...【详细内容】
2024-01-30    简易百科  Tags:VPS服务器   点击:(71)  评论:(0)  加入收藏
VPS服务器下载速度慢?这五招帮你提速
VPS服务器下载速度慢可能会让用户感到沮丧,尤其是对于需要大量下载和上传数据的用户。幸运的是,有一些方法可以帮助您提高VPS服务器的下载速度,使您的在线体验更加顺畅。在本文...【详细内容】
2024-01-30  IDC行业观察者    Tags:VPS服务器   点击:(58)  评论:(0)  加入收藏
美国VPS和英国VPS:地理位置对服务器性能的影响
在今天的数字时代,VPS已成为在线业务和网站托管的关键组成部分。然而,选择合适的VPS主机服务时,地理位置通常被忽视,尽管它对服务器性能有着重要的影响。本文将探讨美国VPS和英...【详细内容】
2024-01-26  IDC行业观察者    Tags:服务器   点击:(55)  评论:(0)  加入收藏
如何判断服务器所需带宽:基于业务需求和流量模式的关键考量
在选择服务器时,带宽是一个重要的考虑因素。带宽的大小直接影响到网站的加载速度和用户的访问体验。那么,如何判断服务器需要多大的带宽呢?本文将为你揭示这一关键问题的答案...【详细内容】
2024-01-26  源库科技    Tags:服务器   点击:(75)  评论:(0)  加入收藏
服务器内存空间及IO操作原理解析
服务器的内存空间分为内核空间和用户空间,而我们编写的程序通常在用户空间中运行。在进行读写操作时,我们直接操作的是用户缓冲区,而用户缓冲区的内容来自于内核缓冲区。这种内...【详细内容】
2024-01-23  王建立    Tags:服务器   点击:(44)  评论:(0)  加入收藏
如何在Java环境中安装Nginx?
1. 下载Nginx:首先,前往Nginx官方网站(https://nginx.org/en/download.html)下载新版本的Nginx。选择适合您操作系统的版本,通常有Windows、Linux和Mac等不同操作系统的版本可供...【详细内容】
2024-01-22  敲代码的小动    Tags:Nginx   点击:(63)  评论:(0)  加入收藏
服务器证书和SSL证书有啥区别?
在互联网经济时代,随着越来越多的信息以及合作都是从企业官网开始的,因此绝大多数企业都会为自己的网站配置SSL证书,以提高安全性。在接触SSL证书时,也有很多人称之为服务器证书...【详细内容】
2024-01-10  安信SSL证书    Tags:服务器证书   点击:(65)  评论:(0)  加入收藏
宝塔面板怎样部署java项目?
宝塔面板怎样部署java项目?在使用宝塔面板部署Java项目之前,需要确保已经安装了Java Development Kit (JDK)。接下来,将介绍如何使用宝塔面板来部署Java项目的步骤。步骤一:安装...【详细内容】
2024-01-09  西部数码    Tags:宝塔面板   点击:(115)  评论:(0)  加入收藏
站内最新
站内热门
站内头条