强如 Disruptor 也发生内存溢出?

前言
OutOfMemoryError 问题相信很多朋友都遇到过,相对于常见的业务异常(数组越界、空指针等)来说这类问题是很难定位和解决的。
本文以最近碰到的一次线上内存溢出的定位、解决问题的方式展开;希望能对碰到类似问题的同学带来思路和帮助。
主要从表现-->排查-->定位-->解决 四个步骤来分析和解决问题。
表象
最近我们生产上的一个应用不断的爆出内存溢出,并且随着业务量的增长出现的频次越来越高。
该程序的业务逻辑非常简单,就是从 Kafka 中将数据消费下来然后批量的做持久化操作。
而现象则是随着 Kafka 的消息越多,出现的异常的频次就越快。由于当时还有其他工作所以只能让运维做重启,并且监控好堆内存以及 GC 情况。
重启大法虽好,可是依然不能根本解决问题。
排查
于是我们想根据运维之前收集到的内存数据、GC 日志尝试判断哪里出现问题。

结果发现老年代的内存使用就算是发生 GC 也一直居高不下,而且随着时间推移也越来越高。
结合 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
你的点赞与转发是最大的支持。
强如 Disruptor 也发生内存溢出?的更多相关文章
- 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码
程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...
- 【Itext】解决Itext5大并发大数据量下输出PDF发生内存溢出outofmemery异常
尼玛,这个问题干扰了我两个星期!! 关键字 itext5 outofmemery 内存溢出 大数据 高并发 多线程 pdf 导出 报表 itext 并发 在读<<iText in Acti ...
- c# 多线程里面创建byte数组发生内存溢出异常求解
在多线程里面读取一个400多M的Xml文件,首先将其读入FileStream里面,然后,在执行 byte [] bts = new byte[fs.Length]; 这句代码时,出现内存溢出的异常,求 ...
- JVM 中发生内存溢出的 8 种原因及解决办法
1. Java 堆空间 2. GC 开销超过限制 3. 请求的数组大小超过虚拟机限制 4. Perm gen 空间 5. Metaspace 6. 无法新建本机线程 7. 杀死进程或子进程 8. 发生 ...
- JVM 发生内存溢出的 8 种原因、及解决办法
阅读本文大概需要 2.3 分钟. 出处:割肉机 cnblogs.com/williamjie/p/11164572.html Java 堆空间 GC 开销超过限制 请求的数组大小超过虚拟机限制 Per ...
- 性能分析 | JVM发生内存溢出的8种原因及解决办法
推荐阅读:史上最详细JVM与性能优化知识点综合整理 1.Java 堆空间 2.GC 开销超过限制 3.请求的数组大小超过虚拟机限制 4.Perm gen 空间 5.Metaspace 6.无法新建本机 ...
- viewPager--viewpager时,发生内存溢出OOM问题
两个问题:1.如果图片达到500kb每张,你这个划屏会有顿卡:2.快速滑动有出现0.几秒的白屏.图片越大,顿卡越明显. 回复parcool:500kb的背景算大的了,如果是想做图片墙,viewpage ...
- 【转载】Android 内存溢出如何发生的。
[转载]Android 内存溢出如何发生的. 且谈Android内存溢出 前言 关于android的内存溢出在创新文档库中也有不少,网络上也有很多这方面的资料.所以这遍文章不算是正真意义上的创新,仅仅 ...
- Android之内存泄露、内存溢出、内存抖动分析
内存 JAVA是在JVM所虚拟出的内存环境中运行的,内存分为三个区:堆.栈和方法区.栈(stack):是简单的数据结构,程序运行时系统自动分配,使用完毕后自动释放.优点:速度快.堆(heap) ...
随机推荐
- 高精度POJ1001
今天看到这道题了 poj1001 题目地址是http://bailian.openjudge.cn/practice/1001/ 英文看得懂,可是算法不明白,所以转别人的文章,留着给学生看看:乔高建( ...
- AnjularJs教程
原文地址:https://www.angular.cn/guide/quickstart#step-1-install-the-angular-cli
- jmeter主要函数助手功用说明
jmeter中虽然有很多的插件,但是有些需要安装,有些具有一定的局限性.函数助手是一个快捷的工具库.下面记录一下函数助手中一些主要的函数的使用方法. 注:不内容中所有的实例均基于3.2记录 1._Be ...
- may be a diary?
[About Me] SD某弱校高二的OIer. qq 995681518,欢迎一起交流~ 喵喵喵喵喵 "当你想要颓废的那一刻,想一想当初为什么走到了这里." 以下文字充满负面情绪 ...
- 「雅礼集训 2017 Day5」矩阵
填坑填坑.. 感谢wwt耐心讲解啊.. 如果要看这篇题解建议从上往下读不要跳哦.. 30pts 把$A$和$C$看成$n$个$n$维向量,那$A_i$是否加入到$C_j$中就可以用$B_{i,j}$表 ...
- VB读写进程的内存
在窗体部分简单测试了ReadProcessMemory和WriteProcessMemory对另一个程序进程的读写. 由于临时项目变动,又不需要了,所以直接封类,删工程.以下代码没有一个函数经过测试, ...
- git教程——安装配置
Git(读音为/gɪt/.)是一个开源的分布式版本控制系统,可以有效.高速的处理从很小到非常大的项目版本管理. Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个 ...
- .Net Core 部署 CentOs7+Nginx
先爆图 由于是初学者,部署出来这个界面也不容易,此前第一步弄了个这个出来 动态的没问题,然后静态资源死活就是不出来,弄了两个小时没有结果,带着遗憾睡了个觉 试验1: server { listen ; ...
- [HACK] docker runtime 挂载宿主机目录
网上看到的很多所谓的挂载都是容器创建时期的挂载,而且参数都不清不楚,整理如下(--name别名自己加): docker run -v /src/path:/dest/path:rw ${IMAGE} ...
- Spring Security中html页面设置hasRole无效的问题
Spring Security中html页面设置hasRole无效的问题 一.前言 学了几天的spring Security,偶然发现的hasRole和hasAnyAuthority的区别.当然,可能 ...