内存溢出满分排查步骤,看完不用担心拿不到 Offer 了 - 今日头条

本文由 简悦 SimpRead 转码, 原文地址 www.toutiao.com

行行行,诶真实拿你们没办法,那我就带大家回温一下 JVM 的内存模型今天我就直说堆,因为溢出是发送在堆中的。

上次给老公们说过了死循环 cpu 飙高的排查过程,今天就带着老公们看看堆内存溢出我们一般怎么排查的。

在排查之前,我想 jvm 的基础知识大家应该都是了解了的吧?

老婆我就是不了解,人家要你说给我听。

行行行,诶真实拿你们没办法,那我就带大家回温一下 JVM 的内存模型(这玩意跟 JAVA 内存模型 JMM 可不一样,不要记错了)

今天我就直说堆,因为溢出是发送在堆中的。

JVM 堆内存被分为两部分:年轻代(Young Generation)和老年代(Old Generation)。

年轻代

年轻代是所有新对象产生的地方。当年轻代内存空间被用完时,就会触发垃圾回收。这个垃圾回收叫做 Minor GC。

年轻代被分为 3 个部分——Enden 区和两个 Survivor 区。

年轻代空间的要点:

  1. 大多数新建的对象都位于 Eden 区。
  2. 当 Eden 区被对象填满时,就会执行 Minor GC,并把所有存活下来的对象转移到其中一个 survivor 区。
  3. Minor GC 同样会检查存活下来的对象,并把它们转移到另一个 survivor 区。这样在一段时间内,总会有一个空的 survivor 区。
  4. 经过多次 GC 周期后,仍然存活下来的对象会被转移到年老代内存空间,通常这是在年轻代有资格提升到年老代前通过设定年龄阈值来完成的。

年老代

年老代内存里包含了长期存活的对象和经过多次 Minor GC 后依然存活下来的对象,通常会在老年代内存被占满时进行垃圾回收。

GC 种类

Major GC

老年代的垃圾收集叫做 Major GC,Major GC 通常是跟 full GC 是等价的,收集整个 GC 堆。

分代 GC

  1. Young GC:只收集年轻代的 GC
  2. Old GC:只收集年老代的 GC(只有 CMS 的 concurrent collection 是这个模式)
  3. Mixed GC:收集整个 young gen 以及部分 old gen 的 GC(只有 G1 有这个模式)

Full GC

Full GC 定义是相对明确的,就是针对整个新生代、老生代、元空间(metaspace,java8 以上版本取代 perm gen)的全局范围的 GC。

https://p9.toutiaoimg.com/origin/pgc-image/84cfe8db32ad41bfa1c5017ad2e7701c?from=pc

老公们可以从上图看到年轻代分为了一个 Eden 区和两个 survivor 区(S1,S2),survivor 区同一时间只会有一个满一个空,交替的。

然后就是 GC 到一定的阈值到老年代,今天不讲永久代所以忽略 Mataspace。

老婆:那怎么分析呢?

今天我就用一个 JDK 自带的工具 jvisualvm 来给大家演示一波怎么操作的,因为这玩意谁都有,你去命令行敲一下 jvisualvm 就出来了(Mac 是这样的,不知道 Windows 是怎么样子的)。

操作界面:

https://p9.toutiaoimg.com/origin/pgc-image/eb8ac1613a76414b85f901502672936c?from=pc

一般什么情况可能是出现了溢出呢?

超时,不进行服务,服务挂掉,接口不在服务这样的异常问题。

那模拟也很简单,我写个循环一直往 List 丢数据,不使用 list 就能看到现象了

https://p9.toutiaoimg.com/origin/pgc-image/d9e5a04a61e740b4a91e01036fd1d34c?from=pc

老公们可以看到图形化界面还是很清晰明了的,这个是 Visual GC 的插件

https://p9.toutiaoimg.com/origin/pgc-image/588fea5a5c94417aaf25f7104dd96c08?from=pc

大家点击菜单栏的插件,然后安装就好了,安装完了记得点击激活。

https://p9.toutiaoimg.com/origin/pgc-image/a4dcdf0940c94c1baaf6f719ca43299a?from=pc

可以看到不释放,堆空间就一直上去,直到 OOM(out of memory)

https://p9.toutiaoimg.com/origin/pgc-image/9924c371718341d0a75988ea1304be6b?from=pc

这个时候我们就 dump 下来堆信息看看

https://p9.toutiaoimg.com/origin/pgc-image/cbc68d3520a44d3187af40be4b1627bf?from=pc

会 dump 出一个这样的 hprof 快照文件,可以用 jvisualvm 本身的系统去分析,我这里推荐 MAT 吧,因为我习惯这个了。

https://p9.toutiaoimg.com/origin/pgc-image/2ca3c2867501426fbcf559d6295725d4?from=pc

MAT :下载地址

下来好了我们可以看到 mat 已经分析了我们的文件

https://p9.toutiaoimg.com/origin/pgc-image/958cdff0b25940eaaa0cfa65c099db01?from=pc

你看他就是个暖男,都帮我们分析出来了一个问题,我们点进去看看

https://p9.toutiaoimg.com/origin/pgc-image/fec033d9a0814a49926c9f28e9b52eba?from=pc

他发现了是 ArrayList 的问题了,我们再往下看看

https://p9.toutiaoimg.com/origin/pgc-image/5ff3f173fd4c464cb99e86bde29a28f0?from=pc

看到了嘛,具体代码的位置都帮我们定位好了,那排查也就是手到擒来的事情了。

https://p9.toutiaoimg.com/origin/pgc-image/2d129270a8984cb2af007901c2eb1dcd?from=pc

延伸点

上面我们使用工具 jump 了,那怎么去服务器上 jump 呢?

1
jmap -dump:format=b,file=<dumpfile.hprof> <pid>

有老公可能问了,不是所有的故障当时我们都在场的,无法及时 jump,那也简单

1
-XX:+HeapDumpOnOutOfMemoryError

配置这玩意之后,oom 的时候会自动 jump 的,到时候拿快照分析一波就好了。

MAT 的功能还有很多的,百度谷歌太多工具文了,我就不做重复的工作了,比如还可以排查对象的强弱引用,还可以查看引用链等等。

还有个只写这么点的原因是有点晚了,顶不住了,最近不拍视频也是因为事情多了,有点小忙,希望老公们体量,对了 Redis 的分布式锁已经在安排的路上了。

我是敖丙,一个在互联网苟且偷生的工具人。

最好的关系是互相成就老公们的 「三连」 就是丙丙创作的最大动力,我们下期见!

注:如果本篇博客有任何错误和建议,欢迎老公们留言,老公你快说句话啊

你知道的越多,你不知道的越多