Disruptor挖坑MemoryAnalyzer来填
- 1、起因背景
- 2、初步定位
- 3、细化定位
- 3.1、内存文件导出命令
- 3.2、MemoryAnalyzer分析xxx.hprof文件
- 4、思考
- 5、花絮
1、起因背景
博主练手写的并发项目订单服务出现了程序一直处于加载未完成的状态,电脑温度升高,CPU使用率飙高,风扇狂呼,一时摸不着头脑。 |
2、初步定位
于是,采用代码回滚排查法,发现是由于bufferSize多乘以一个1024。同时断点停留在了Disruptor<OrderEntity> disruptor = new Disruptor<>(factory, bufferSize, threadPoolExecutor, ProducerType.SINGLE, new BlockingWaitStrategy());
这块代码,idea的状态看起来还是运行状态,也没有报OOM。
于是博主第一时间跟踪进去,发现了一个比较有意思的地方:
没错this.entries.length的值是2,097,152,也就是1024*1024*2。
博主就把断点设置在this.entries[i] = eventFactory.newInstance();
这一行,同时为改断点设置条件(条件断点)
这里我们可以采用量级感知代码是否在此处循环运行:i的值可以先是100,再1000、10000、100000~~随着量级的增大满足断点的耗时也越来越长,博主曾在某个量级停留了5 ~ 10分钟。直到博主停留了一个多小时,还是没有达到断点设置的那个条件。此时,博主遭不住了,虽然代码回滚排查法+量级感知判断出了这个问题出现的地方。但是博主还不满足,博主想用数据知道是不是由于这段代码多循环创建对象导致的内存占用过大。本来想用jconsole排查,但是jconsole这种工具连不上这种未完成启动完毕状态下的程序。于是博主打算将线程在循环创建对象的内存文件导出来,再用MemoryAnalyzer分析。
3、细化定位
3.1、内存文件导出命令
jps #获取java进程的pid(进程号)
jmap -dump:live,format=b,file=heap.hprof pid(进程号)
如果是在linux中的.hprof文件比较大的话,可以先压缩,再sz到windows。
博主这个文件差不多6G。
3.2、MemoryAnalyzer分析xxx.hprof文件
由于MemoryAnalyzer的配置文件MemoryAnalyzer.ini默认的内存大小配置是-Xmx1024m
,所以我们6G的文件打开会报如下错误。
所以我们给10g给它:
现在我们可以正常打开文件了
看这堆的占用,的确有点多。
右键这个线程,看调用栈:
到这里,真相也就浮出水面了
竖型蓝色框中看到的create,<init>这些,基本就是构造方法创建对象了,还有那个fill方法,不就是我们前面提到的多循环创建对象那段代码吗;还有更准确的我们客户端调用的地方:横型蓝色框
代码行数都给你标出来了:
要到这里,真实数据也验证了我们的判断。
4、思考
- 循环代码要着重测试循环次数
- 出现问题擅于使用各种排查技巧,例如代码回滚排查法
5、花絮
起因背景出现的原因是博主针对Disruptor框架压测发现该框架生产了几千条数据但仅消费了几十条数据,同时后面将jemeter停止压测后手动postman发请求也只是生产数据没有消费,因此结合此次问题排查,后续会发布Disruptor框架使用误区和源码解析,以及更多的jvm内存分析和cpu使用率过高的工具介绍和分析手段。 |