OutOfMemoryError
问题相信不少朋友都遇到过,相对于常见的业务异常(数组越界、空指针等)来讲这类问题是很难定位和解决的。java
本文以最近碰到的一次线上内存溢出的定位、解决问题的方式展开;但愿能对碰到相似问题的同窗带来思路和帮助。git
主要从表现-->排查-->定位-->解决
四个步骤来分析和解决问题。github
<!--more-->数组
最近咱们生产上的一个应用不断的爆出内存溢出,而且随着业务量的增加出现的频次愈来愈高。服务器
该程序的业务逻辑很是简单,就是从 Kafka 中将数据消费下来而后批量的作持久化操做。运维
而现象则是随着 Kafka 的消息越多,出现的异常的频次就越快。因为当时还有其余工做因此只能让运维作重启,而且监控好堆内存以及 GC 状况。测试
重启大法虽好,但是依然不能根本解决问题。
因而咱们想根据运维以前收集到的内存数据、GC 日志尝试判断哪里出现问题。spa
结果发现老年代的内存使用就算是发生 GC 也一直居高不下,并且随着时间推移也愈来愈高。3d
结合 jstat 的日志发现就算是发生了 FGC 老年代也已经回收不了,内存已经到顶。指针
甚至有几台应用 FGC 达到了上百次,时间也高的可怕。
这说明应用的内存使用确定是有问题的,有许多赖皮对象始终回收不掉。
因为生产上的内存 dump 文件很是大,达到了几十G。也是因为咱们的内存设置太大有关。
因此致使想使用 MAT 分析须要花费大量时间。
所以咱们便想是否能够在本地复现,这样就要好定位的多。
为了尽快的复现问题,我将本地应用最大堆内存设置为 150M。
而后在消费 Kafka 那里 Mock 为一个 while 循环一直不断的生成数据。
同时当应用启动以后利用 VisualVM 连上应用实时监控内存、GC 的使用状况。
结果跑了 10 几分钟内存使用并无什么问题。根据图中能够看出,每产生一次 GC 内存都能有效的回收,因此这样并无复现问题。
无法复现问题就很难定位了。因而咱们 review 代码,发现生产的逻辑和咱们用 while 循环 Mock 数据还不太同样。
查看生产的日志发现每次从 Kafka 中取出的都是几百条数据,而咱们 Mock 时每次只能产生一条。
为了尽量的模拟生产状况便在服务器上跑着一个生产者程序,一直源源不断的向 Kafka 中发送数据。
果真不出意外只跑了一分多钟内存就顶不住了,观察左图发现 GC 的频次很是高,可是内存的回收倒是相形见拙。
同时后台也开始打印内存溢出了,这样便复现出问题。
从目前的表现来看就是内存中有许多对象一直存在强引用关系致使得不到回收。
因而便想看看究竟是什么对象占用了这么多的内存,利用 VisualVM 的 HeapDump 功能能够当即 dump 出当前应用的内存状况。
结果发现 com.lmax.disruptor.RingBuffer
类型的对象占用了将近 50% 的内存。
看到这个包天然就想到了 Disruptor
环形队列。
再次 review 代码发现:从 Kafka 里取出的 700 条数据是直接往 Disruptor 里丢的。
这里也就能说明为何第一次模拟数据没复现问题了。
模拟的时候是一个对象放进队列里,而生产的状况是 700 条数据放进队列里。这个数据量是 700 倍的差距。
而 Disruptor 做为一个环形队列,再对象没有被覆盖以前是一直存在的。
我也作了一个实验,证实确实如此。
我设置队列大小为 8 ,从 0~9 往里面写 10 条数据,当写到 8 的时候就会把以前 0 的位置覆盖掉,后面的以此类推(相似于 HashMap 的取模定位)。
因此在生产上假设咱们的队列大小是 1024,那么随着系统的运行最终确定会致使 1024 个位置上装满了对象,并且每一个位置是 700 个!
因而查看了生产上 Disruptor 的 RingBuffer 配置,结果是:1024*1024
。
这个数量级就很是吓人了。
为了验证是不是这个问题,我在本地将该值换为 2 ,一个最小值试试。
一样的 128M 内存,也是经过 Kafka 一直源源不断的取出数据。经过监控以下:
跑了 20 几分钟系统一切正常,每当一次 GC 都能回收大部份内存,最终呈现锯齿状。
这样问题就找到了,不过生产上这个值具体设置多少还得根据业务状况测试才能知道,但原有的 1024*1024 是绝对不能再使用了。
虽然到了最后也就改了一行代码(还没改,直接修改配置),但这排查过程我以为是有意义的。
也会让大部分以为 JVM 这样的黑盒难如下手的同窗有一个直观的感觉。
同时也得感叹 Disruptor 东西虽好,也不能乱用哦!
相关演示代码查看:
https://github.com/crossoverJie/JCSprout/tree/master/src/main/java/com/crossoverjie/disruptor
你的点赞与转发是最大的支持。