作家
登录

解Bug之路:记一次JVM堆外内存泄露Bug的查找

作者: 来源: 2018-01-16 19:28:36 阅读 我要评论

年前最后一场技巧盛宴 | 1月27日与京东、日记易技巧大年夜咖畅聊智能化运维成长趋势!

媒介

JVM的堆外内存泄漏的定位一向是个比较棘手的问题。此次的Bug查找大年夜堆内内存的泄漏反推出堆外内存,同时对物理内存的应用做了定量的分析,大年夜而实锤了Bug的源徒辜笔者将此Bug分析的过程写成博客,以飨读者。
因为物理内存定量分析部分用到了linux kernel虚拟内存治理的常识,读者如不雅有兴趣懂得请看ulk3(《深刻懂得linux内核第三版》)

内存泄漏Bug现场

一个线上稳定运行了三年的体系,大年夜物理机迁徙到docker情况后,运行了一段时光,忽然被监控体系发出了某些实例弗采取的报警。所幸有负载均衡,可以主动下掉落节点,如下图所示:

gc_local
登录到对应机械上后,发明因为内存占用太大年夜,触发OOM,然后被linux体系本身给kill了。

应急办法

当前设置的最大年夜堆内存是1792M,如下所示:

-Xmx1792m -Xms1792m -Xmn900m -XX:PermSize=256m -XX:MaxPermSize=256m -server -Xss512k 

查看操作体系层面的监控,发明内存占用情况如下图所示:

gc_upper

上图蓝色的线表示总的内存应用量,发明一向涨到了4G后,超出了体系限制。
很明显,有堆外内存泄漏了。

查找线索

gc日记


一般出现内存泄漏,笔者立马想到的就是查看当时的gc日记。
本身应用所采取框架会准时打印出对应的gc日记,遂查看,发明gc日记一切正常。对应日记如下:

gc_log

查看了当天的所有gc日记,发明内存始终会回落到170M阁下,并无明显的增长。要知道JVM过程本身占用的内存可是接近4G(加上其它过程,例如日记过程就已经到4G了),进一步确认是堆外内存导致。

排查代码

打开线上办事对应对应代码,查了一圈,发明没有任何处所显式应用堆外内存,其没有依附任何额外的native办法。关于收集IO的代码也是托管给Tomcat,很明显,作为一个全世界广泛风行的Web办事器,Tomcat不大年夜可能有堆外内存泄漏。

进一步查找

因为线上出问题的Server已经被kill,还好有其它几台,登上去发明它们也 占用了很大年夜的堆外内存,只是还没有到触发OOM的临界点罢了。于是就赶紧用jmap dump了两台机械中应用JVM的堆情况,这两台留做现场保存不动,然后将其它机械敏捷重启,以防同时被OOM导致办事弗采取。
应用如下敕令dump:

jmap -dump:format=b,file=heap.bin [pid]

应用MAT分析Heap文件

挑了一个heap文件进行分析,堆的应用情况如下图所示:

一共用了200多M,和之前gc文件打印出来的170M相差不大年夜,远远没有到4G的程度。

紧急在出问题的实例上再次启动应用,启动后,内存占用正常,一切Okay。

奇怪现象

不得不说MAT是个异常好用的对象,它可以提示你可能内存泄漏的点:

gc_cached_bns_client

这个cachedBnsClient类有12452个实例,占用了全部堆的61.92%。

查看了另一个heap文件,发明也是同样的情况。这个处所肯定有内存泄漏,然则也占用了130多M,和4G相差甚远。

查看对应的代码

体系中大年夜部分对于CachedBnsClient的调用,都是经由过程注解Autowired的,这部分实例数很少。
独一频繁产生词攀类实例的代码如下所示:

@Override    public void fun() {            BnsClient bnsClient = new CachedBnsClient();          // do something    return  ;}

此CachedBnsClient仅仅在办法体内应用,并没有逃逸到外面,再看词攀类本身

没有任何static变量,同时也没有往任何全局变量注册自身。换言之,在类的成员(Member)中,是弗成能出现内存泄漏的。
当时只粗略的过了一过成员变量,回过火来细想,照样漏了不少处所的。

更多信息

现场信息越多,越能找出蛛丝马迹。先用jstack把线程信息dump下来看下。 这一看,立马发清楚明了不合,除了正常的IO线程以及框架本身的一些守护线程外,竟然还多出来了12563多个线程。

"Thread-5" daemon prio=10 tid=0x00007fb79426e000 nid=0x7346 waiting>再次check对应代码

本来刚才看CachedBnsClient代码的时刻漏掉掉落了一个关键的点!

    public CachedBnsClient(BnsClient client) {        super();        this.backendClient = client;        new Thread() {            @Override            public void run() {                for (; ; ) {                    refreshCache();                    try {                        Thread.sleep(60 * 1000);                    } catch (InterruptedException e) {                        logger.error("掉足", e);                    }                }            }    }

这段代码是CachedBnsClient的构造函数,其在琅绫擎创建了一个无穷轮回的线程,每隔60s启动一次刷新一下琅绫擎的缓存!


  推荐阅读

  解决Plesk面板Nginx不能自动启动的问题

年前最后一场技巧盛宴 | 1月27日与京东、日记易技巧大年夜咖畅聊智能化运维成长趋势! 问题描>>>详细阅读


本文标题:解Bug之路:记一次JVM堆外内存泄露Bug的查找

地址:http://www.17bianji.com/lsqh/40306.html

关键词: 探索发现

乐购科技部分新闻及文章转载自互联网,供读者交流和学习,若有涉及作者版权等问题请及时与我们联系,以便更正、删除或按规定办理。感谢所有提供资讯的网站,欢迎各类媒体与乐购科技进行文章共享合作。

网友点评
自媒体专栏

评论

热度

精彩导读
栏目ID=71的表不存在(操作类型=0)