作家
登录

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

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

找到关键点

因为在代码层面没有发明堆外内存的陈迹,那就持续找些其它的信息,欲望能发明蛛丝马迹。

Dump出JVM的Heap堆

在看到12452个等待在CachedBnsClient.run的营业的一刹时笔者就意识到,肯定是这边的线程导致对外内存泄漏了。下面就是根据线程大年夜小寂?驿泄漏内存量是不是确切可以或许引起OOM了。

发明内存计算对不上

因为我们这边设置的Xss是512K,即一个线程栈大年夜小是512K,而因为线程共享其它MM单位(线程本地内存是是如今线程栈上的),所以实际线程堆外内存占用数量也是512K。进行如下计算:

12563 * 512K = 6331M = 6.3G

全部情况一共4G,加上JVM堆内存1.8G(1792M),已经明显的超过了4G。

如不雅按照此计算,应用应用早就被OOM了。

怎么回事呢?

为懂得决这个问题,笔者又思虑了良久。如下所示:

Java线程底层实现

(6.3G + 1.8G)=8.1G > 4G

JVM的线程在linux上底层是调用NPTL(Native Posix Thread Library)来创建的,一个JVM线程就对应linux的lwp(轻量级过程,也是过程,只不过共享了mm_struct,用来实现线程),一个thread.start就相当于do_fork了一把。

个中,我们在JVM启动时刻设置了-Xss=512K(即线程栈大年夜小),这512K中然后有8K是必须应用的,这8K是由过程的内核栈和thread_info公用的,放在两块持续的物理页框上。如下图所示:

gc_thread_task

众所周知,一个过程(包含lwp)包含内核栈和用户栈,内核栈+thread_info用了8K,那么竽暌姑户态的┞坊可用内存就是:

512K-8K=504K

如下图所示:

gc_kernel_user

Linux现什物理内存映射

事实上linux对物理内存的应用异常的抠门,一开端只是分派了虚拟内存的线性区,并没有分派实际的物理内存,只有推到最后应用的时刻才分派具体的物理内存,即所谓的请求调页。如下图所示:

gc_do_page_fault

查看smaps过程内存应用信息

应用如下敕令,查看

cat /proc/[pid]/smaps > smaps.txt

现什物理内存应用信息,如下所示:

搜刮下504KB,正好是12563个,对了12563个线程,个中Rss表示现什物理内存(含共享库)92KB,Pss表示现什物理内存(按比例共享库)92KB(因为没有共享库,所以Rss==Pss),以第一个7fa69a6d1000-7fa69a74f000线性区来看,其映射了92KB的空间,第二个映射了152KB的空间。如下图所示:

因为代码排查下来,感到这块不该该出现内存泄漏(然则事实确是如斯的打脸)。这个类也没有显式用到堆外内存,并且只占了130M,和4G比起来眇乎小哉,照样先去追查重要抵触再说。

应用jstack dump线程信息

gc_mem

gc_heap_dump

7fa69a6d1000-7fa69a74f000 rwxp 00000000 00:00 0 Size:                504 kBRss:                  92 kBPss:                  92 kBShared_Clean:          0 kBShared_Dirty:          0 kBPrivate_Clean:         0 kBPrivate_Dirty:        92 kBReferenced:           92 kBAnonymous:            92 kBAnonHugePages:         0 kBSwap:                  0 kBKernelPageSize:        4 kBMMUPageSize:           4 kB7fa69a7d3000-7fa69a851000 rwxp 00000000 00:00 0 Size:                504 kBRss:                 152 kBPss:                 152 kBShared_Clean:          0 kBShared_Dirty:          0 kBPrivate_Clean:         0 kBPrivate_Dirty:       152 kBReferenced:          152 kBAnonymous:           152 kBAnonHugePages:         0 kBSwap:                  0 kBKernelPageSize:        4 kBMMUPageSize:           4 kB

挑出相符前提(即size是504K)的几十组看了下,根本都在92K-152K之间,再加上内核栈8K

(92+152)/2+8K=130K,因为是估算,取整为128K,即反竽暌钩此应用平均线程栈大年夜小。

留意,实际内存有波动的原因是因为情况不合,大年夜而走了不合的分支,导致栈上的增长不合。

从新进行内存计算

-Xmx1792m -Xms1792m

即1.8G的堆内内存,这里是即时分派,一开端就用物理页框填充。
12563个线程,每个线程栈平均大年夜小128K,即:

128K * 12563=1570M=1.5G的对外内存
public class CachedBnsClient   {    private ConcurrentHashMap<String, List<String>> authCache = new ConcurrentHashMap<String, List<String>>();    private ConcurrentHashMap<String, List<URI>> validUriCache = new ConcurrentHashMap<String, List<URI>>();    private ConcurrentHashMap<String, List<URI>> uriCache = new ConcurrentHashMap<String, List<URI>>();......}

取个整数128K,就能反竽暌钩出平均程度。再拿这个128K * 12563 =1570M = 1.5G,加上JVM的1.8G,就已经达到了3.3G,再加上kernel和日记传输过程等应用的内存数量,确切已经接近了4G,如许内存就对应上了!(注:用于定量内存计算的情况是一台内存用量将近4G,但还没OOM的机械)


  推荐阅读

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

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


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

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

关键词: 探索发现

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

网友点评
自媒体专栏

评论

热度

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