干货:一次内存泄露的实战分析过程
一、背景介绍
最近在做日志服务的稳定性测试,大约跑一个小时左右一直报OOM,使用的测试java代码结构跟NDIR稳定性测试使用的基本上是一致的,但是NDIR稳定性测试做过很多次了,没有出现过类似问题,由于急着做稳定性测试,这个事情让我很头疼,于是花了一些时间去分析了一下内存情况。
二、问题分析
1.为什么NDIR可以使用,日志服务却不可以?
我是这么分析的:
第一,我考虑了两种稳定性测试请求情况的差别,主要是从并发线程数目、tps值以及交互的数据量出发,发现两个测试框架的线程数目以及请求时实际的tps值大致是一个量级的,而交互的数据量差别很大,表现出来的情况是日志服务这边打印的日志量过大,基本上是NDIR那边10倍以上的日志量。
第二,我考虑了两种稳定性测试配置上的差异,主要是从机器配置、mvn配置、jvm配置情况出发,发现这个方面是配置是一样的,测试机器都是同一个。
第三,我考虑两种稳定性测试代码以及数据结构的差异,发现代码结构以及使用的数据结构也都是差不多的,没有发现什么特殊地方。
所以,可能导致日志服务出现问题的地方最有可能的地方就在交互数据量以及日志量,但是日志系统使用的是log4j,是一个很成熟的工具,应该没有问题。
2.运行时大量消耗内存的到底是什么?
带着上面的初步分析,我开始进行了内存排查这个使内存oom的对象到底是什么。
首先看看我mvn的配置贴出来:<argLine>-Dfile.encoding=UTF-8 -Xms512m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512M</argLine>
再看看实际运行时jvm情况:
实际机器分配的jvm参数与设置的一致,并且perm消耗值在不断的长大。
再看看perm情况以及历史上消耗内存情况:
从图上可以看出消耗内存最多是是一个class [B的对象有4852个对象,如果能知道这个类是什么,那么就可以对症下药消灭它了。关键是这些jdk自带的一些工具不直观,看不出来是哪些类的对象,于是使用了更为直观的大名鼎鼎的eclipse的插件MemoryAnalyzer来分析内存情况。
从这个图中清晰看到基本上都是ByteArrayOutputStream这个类的对象将内存消耗尽了,那我们知道log4j写日志以及maven打印console都是使用这个类的,于是打开去看看。
从这个图中可以很清楚的看出,org.apache.maven.surefire.report.ReporterManager的consoleCapture方法消耗了其中的一半,也就是说console就占用了其中的一半内存,总共512M的term被这个console打印占去了260M,那剩下的肯定就不够用了,而且这些byte[]是持久不释放的,那么不管你有多少内存,只要运行时间够长一定会将内存耗尽的。而这个时候去log4j的配置文件看一下发现:
那么情况就水落石出了,也能很好的解释为什么同样的稳定性测试框架NDIR能很好的运行日志服务就不可以,原因是NDIR那边打印的日志量太小,三天到五天的稳定性测试不足以触发perm的512M大小内存OOM的上限,如果继续一直跑下去相信也是会发生OOM的情况的。花费了两天和一个晚上的时间终于把问题解决了,这种成就感真的是比发薪水还要爽上N倍。
3.怎么解决?知道问题的原因后,解决问题就很简单了。第一种方法,跑稳定性测试的时候去除console配置,这样就不会有这种情况的发生。第二种办法,大力的减少日志打印量,这样可以跑的时间久很多,但是治标不治本。
三、总结
在我们实际的测试过程中可能会遇到很多的问题,这里总结为四点:
1.增强分析问题的能力。
2.增强掌握的知识和技能,学为所用。这是一个积累的过程,不要怕麻烦,慢慢积累知识和工具,试着去解决自己遇到的问题,感觉慢慢会有收货。在这里解决这个问题带来的那种感觉真的很棒。
3.我觉得遇到问题时,不要急于去做,留一些时间去思考也许结果会更好。在遇到这个问题的时候,因为是着急着上线,着急着做稳定性测试,所以就急忙着调试,各种尝试以及去网上搜集资料,但始终没有解决问题还花费掉一天一夜的时间,也没有什么收货,而第二天就静下心来去分析问题,然后借助工具去排查问题,结果半天就解决了。
不定期发布有态度不忽悠的靠谱的文章,有内涵,有节操。
喜欢的朋友欢迎关注和转发
本文章为作者原创
🈲禁止🈲
其他公众账号转载,若有转载,请标明出处