tsync堆外内存溢出排查经过

一、发生得问题
tsync服务总是莫名得宕机,java进程被莫名其妙的消失了。

二、查找问题
当时看了系统日志:
sudo -u admin dmesg|grep -A20 kill
在这里插入图片描述
screenshot
发现是oom了,内存不足被系统kill掉了。
当时怀疑有可能是堆内内存溢出,查看监控,系统物理内存:
在这里插入图片描述

发现物理内存确实使用了100%。
继续看java堆内存:
screenshot
发现java堆内内存很正常old区就一直没有满过。
这个时候怀疑可能堆外内存溢出(此后一段时间也注意看了下java进程RES一直在增长直到涨到物理内存又会被系统kill掉)。后来dump出了当时内存要涨满时的堆内存快照,上传到了zprofiler里,所有内存使用都很正常。所以已经确定是堆外内存溢出了。
java堆外溢出最可能得是DirectMemory,正好tsync就会用到nio,所以怀疑因为占用了DirectMemory,因为old区一直没有回收所以有可能是DirectMemory的引用对象一直没有回收导致相应的堆外内存没回收,当时又手动调用了一下fullgc,因为fullgc一直没有发生过,看看内存能不能回收:jmap -histo:live 123345 ,结果java进程的RES还是没有降下来,也就是堆外内存还是没有回收,查看jvm参数发现有个参数DisableExplicitGC 。这个参数禁止了System.gc()方法导致的fullgc,所以我手动回收应该也没有用,如果是这个问题得话定义下MaxDirectMemorySize,并把DisableExplicitGC禁止去掉就应该解决问题了,因为fullgc代价太大查了下资料再增加ExplicitGCInvokesConcurrent 参数,当堆外内存到达MaxDirectMemorySize时不进行fullgc,而是进行cms gc,当时还觉得问题能解决。不过还是看了下所有代码,看到用到nio的bytebuffer都是已经传得false即禁用DirectMemory,又看了下zprofiler里的DirectByteBuffer才几十兆:
screenshot
所以确定应该不是这个问题。
又和pe商量了下,pe给安装了个perftools,打印程序使用的堆外内存快照,跑了一个星期,找出了占用最大得java得native得方法
screenshot
Java_sun_security_ec_ECKeyPairGenerator_generateECKeyPair这个值一直从200多m逐渐上升到1G多。而且对比多个不同时期的文件占用内存一直再上涨都没有释放过……,证明不是瞬发流量突增引起的没有及时释放,是根本就因为不明原因没有释放。

三、定位引用源头
这个native方法是jdk里的,为了查明是业务中哪块代码用的这个类方法,在idea里搜索,没有找到,怀疑是二方包里用的,然后申请了oss的账号和秘钥在一台beta机器上安装btrace 来进行分析,btrace脚本如下:
screenshot
在beta机器上执行./btrace 123345 TraceMemory.java,观察了很久没有打印出调用栈信息,怀疑是脚本写错了把脚本的类和方法改为别的再执行是能成功打印栈信息的,试了其他得也都可以,想到可能是这个包里的类不能被btrace跟踪,请教了下毕玄说也可能是sun包下得不能被trace。没有办法这条路暂时不通,我又想了个办法在日常环境debug那个代码碰碰运气看还有没有调用,正好就debug上了看idea的调用栈如下:
screenshot
原来悟空jar包再引用这个类和方法,原来也怀疑调用的华为jar包应该也有,因为它使用了https,看了下异常日志确实找到了也在使用相关类如下:
screenshot
到这估计已经都找到了引用源头。

四、现在的问题:
为什么这个native方法会内存泄露而不释放,什么样的触发条件导致了这个问题?线上使用的jdk版本是1.7的

猜你喜欢

转载自blog.csdn.net/guo_yang/article/details/83546513