【问题处理】—— 一次内存溢出(OutOfMemoryError)实战排查

简介: 【问题处理】—— 一次内存溢出(OutOfMemoryError)实战排查

项目场景:

某次打开工程前台页面,个别页面直接提示

GC overhead limit exceeded

伴随着的,其余各个页面响应速度都十分缓慢


排查流程

1. GC查看

初步怀疑是内存溢出,但还需要证据,先进入到后台日志查看下


99c2afc1d215416e88ac5bf2acb1710b.png

可以看到后台直接是提示OOM,实锤了内存溢出,我们也可以通过查看GC情况来辅助确定,先看线程id

ps -ef | grep XXXXX(应用名)

得到线程 id26866

然后使用 jstat -gc 26866 5000 进行查看,这个命令的意思是打印线程 26866 的GC 情况,每5秒一次

jstat -gc 26866 5000
• 1

先看倒数第二列:FGCT——full GC time (从应用程序启动到采样时 Full GC 所用的时间(单位秒));

考虑到我是5秒打印一行,,可以看到几乎每时每刻都在做FULL GC,根本没停过,至于FULL GC的效果

可以看这五列

S0 — Heap上的 Survivor space 0 区已使用空间的百分比

S1 — Heap上的 Survivor space 1 区已使用空间的百分比

EU — Heap上的 Eden space 区已使用空间

OU — Heap上的 Old space 区已使用空间

MU — Meta space 区已使用空间

可以看到老年代基本回收不动,新生代能回收,但只能回收一点点,两者都几乎是满空间运行

2. 死锁判断

因为前面判断出GC的问题,死锁有可能占用大量资源无法释放,导致内存溢出,我们先排除是否有线程死锁的情况

jstack 26866

5fb3c78b48c947908e82e9bf7926efb5.png

9e4d315ac51940d4ab20e1eb07a36d36.png

暂未发现其他问题,基本可以排除死锁的情况。那么接下来只能dump下来,对数据进行分析了

3. dump查看

jmap -dump:file=overgc.dump -26866

导出当前堆的情况,存储在文件overgc.dump里。因为我们是开发环境,所以直接从服务器拷贝下来,用jdk自带的jvisualvm来查看该文件

使用jvisualvm打开我们存储的dump文件,此处点击装入快照

因为当时没有截图,所以下面只能以示意图展示,但不影响理解

装载后,通过类,看哪些类占用了太多的空间。一般来说,排名靠前的都是基础的String byte[] 等部分,如果前排有业务类占用太多空间,那么这个类一般就是内存泄漏的罪魁祸首

当时我们发现了某个业务对象的数量达到了10W + ,这在我们的场景中显然是不合理的

然而我们在代码中发现该类有好几个使用的地方,仍旧不好定位,于是切到了jvisualvm的实例部分,通过这些实例去找引用的线程,然后在此处查看线程情况

ffee1fc98aa74bd48d071192d501601d.png

通过分析发现,大量的对象实例是存在List中的,一个List有6000个该对象实例,然而持有这个list的线程却卡死了,经过线程信息 和 代码的相互印证,证实了,这是再把List作为入参,去同步调用一个第三方接口时,第三方接口迟迟没有返回,导致线程一直处于阻塞。

原因分析:

通过上述证据与代码的相互印证,最后还原了事情的原因:

起因是有同事修改了数据库数据,使得原本某条sql应该查出几十条数据,现在却查出6000条。并且在后续代码中,还要把这6000个数据的List,通过RPC,传给第三方接口。

而第三方接口应该也没压测过这么大的数据量,导致迟迟无法返回结果,使我们的线程一直阻塞,给我们前台的表现就是卡住了。

此时,因为前台没响应,于是前台同事又刷新了几次,相当于重新调了该接口,最后的结果就是大量线程查出6000条数据后阻塞在这里,堆里出现该对象的实例超过10W 个,最终OOM了


解决方案:

1. 临时方案:

把同事修在数据库修改的数据进行还原,使得每次SQL查出几十条数据

2. 解决策略:

  1. 排查系统里的SQL,为SQL加上分页或行数限制,mysql的话就是limit,这样保证即使数据库出现大量脏数据,也不会全部查出来
  2. 减少RPC调用的超时时间,原先默认的超时时间是60s ,所以导致我们线程一直在等待返回值,而没有很快报错并释放空间,但对于一个接口来说60s 太长了,即使为了保证接口调通。这个时间用户一般也没耐心等待,最后将RPC的超时时间设定为15s.


目录
相关文章
|
4月前
|
Java 数据库连接
Java中的内存泄漏排查与预防方法
Java中的内存泄漏排查与预防方法
|
4月前
|
监控 Java
Java中的内存泄漏分析与排查技巧
Java中的内存泄漏分析与排查技巧
|
4月前
|
存储 算法 安全
Java面试题:Java内存模型及相关知识点深度解析,Java虚拟机的内存结构及各部分作用,详解Java的垃圾回收机制,谈谈你对Java内存溢出(OutOfMemoryError)的理解?
Java面试题:Java内存模型及相关知识点深度解析,Java虚拟机的内存结构及各部分作用,详解Java的垃圾回收机制,谈谈你对Java内存溢出(OutOfMemoryError)的理解?
70 0
|
4月前
|
存储 监控 算法
LeakCanary 的内存泄露问题排查
LeakCanary 的内存泄露问题排查
68 0
|
1月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
50 2
|
2月前
|
监控 Java Linux
redisson内存泄漏问题排查
【9月更文挑战第22天】在排查 Redisson 内存泄漏问题时,首先需确认内存泄漏的存在,使用专业工具(如 JProfiler)分析内存使用情况,检查对象实例数量及引用关系。其次,检查 Redisson 使用方式,确保正确释放资源、避免长时间持有引用、检查订阅和监听器。此外,还需检查应用程序其他部分是否存在内存泄漏源或循环引用等问题,并考虑更新 Redisson 到最新版本以修复潜在问题。
|
3月前
|
NoSQL Java 测试技术
Golang内存分析工具gctrace和pprof实战
文章详细介绍了Golang的两个内存分析工具gctrace和pprof的使用方法,通过实例分析展示了如何通过gctrace跟踪GC的不同阶段耗时与内存量对比,以及如何使用pprof进行内存分析和调优。
84 0
Golang内存分析工具gctrace和pprof实战
|
3月前
|
JavaScript Java 开发工具
Electron V8排查问题之接近堆内存限制的处理如何解决
Electron V8排查问题之接近堆内存限制的处理如何解决
220 1
|
4月前
|
监控 安全 Java
JVM内存问题之排查Direct Memory泄漏有哪些常用方法
JVM内存问题之排查Direct Memory泄漏有哪些常用方法
119 2
|
4月前
|
监控 Java Linux
JVM内存问题之如果堆内存一直缓慢上涨,如何解决
JVM内存问题之如果堆内存一直缓慢上涨,如何解决
592 1