找到关键点
因为在代码层面没有发明堆外内存的陈迹,那就持续找些其它的信息,欲望能发明蛛丝马迹。
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公用的,放在两块持续的物理页框上。如下图所示:
众所周知,一个过程(包含lwp)包含内核栈和用户栈,内核栈+thread_info用了8K,那么竽暌姑户态的┞坊可用内存就是:
512K-8K=504K
如下图所示:
Linux现什物理内存映射
事实上linux对物理内存的应用异常的抠门,一开端只是分派了虚拟内存的线性区,并没有分派实际的物理内存,只有推到最后应用的时刻才分派具体的物理内存,即所谓的请求调页。如下图所示:
查看smaps过程内存应用信息
应用如下敕令,查看
cat /proc/[pid]/smaps > smaps.txt
现什物理内存应用信息,如下所示:
搜刮下504KB,正好是12563个,对了12563个线程,个中Rss表示现什物理内存(含共享库)92KB,Pss表示现什物理内存(按比例共享库)92KB(因为没有共享库,所以Rss==Pss),以第一个7fa69a6d1000-7fa69a74f000线性区来看,其映射了92KB的空间,第二个映射了152KB的空间。如下图所示:
因为代码排查下来,感到这块不该该出现内存泄漏(然则事实确是如斯的打脸)。这个类也没有显式用到堆外内存,并且只占了130M,和4G比起来眇乎小哉,照样先去追查重要抵触再说。
应用jstack 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的机械)
推荐阅读
年前最后一场技巧盛宴 | 1月27日与京东、日记易技巧大年夜咖畅聊智能化运维成长趋势! 问题描>>>详细阅读
本文标题:解Bug之路:记一次JVM堆外内存泄露Bug的查找
地址:http://www.17bianji.com/lsqh/40306.html
1/2 1