背景知识

Disruptor 相关介绍
https://tech.meituan.com/2016/11/18/disruptor.html
简单来说,可以理解为 Disruptor 内部使用了一个环形的数组结构,本次 OOM 的问题也在环形数组上。

问题排查过程

复现问题

测试反馈反复调用一个接口会导致 OOM ,使用 jmeter 工具进行压力测试,通过 jvisualvm 进行查看,可以看到内存一路上涨,并且无法回收,频繁触发了 FullGC。这样下次,OOM 只是时间问题。

排查思路

问题复现之后,其实就好办了,使用 jmap -dump:live,format=b,file=dump.hprof pid 命令针对 jvm 的内存生成 dump 文件。将 dump 文件用 Eclipse MemoryAnalyzer 打开,选择 Leak Suspects Report 打开分析报告,可以看到 disruptor 的 RingBuffer 占用了绝大多数的内存空间。
image.png

image.png

可以看出来,问题的原因就是这个 EntityData 的 dataList ,每个 Entity 都持有了许多个 EntityData,导致 entries 数组较大。
虽然这些 Entity 对象及其持有的 EntityData 都是可以被 GC 的,但是需要等环形数组写入完毕,最前面的数据被覆盖。不幸的是还没等到 GC 就已经 OOM 了。因此想要解决这个问题,就得让程序能够撑到 GC。

解决思路

现在已经知道是因为环形队列占用的数据太多,导致 GC 无法释放的问题,进一步排查,可以发现有同事在某处打了一行超长的接口回包,导致环形队列数据一路飙升,并且无法回收。经测试,去除该处日志打印即可解决问题。大家日后在日志系统中也应该注意,不要输出无用的日志,尽量只打印关键点,日后定位问题也更加方便。