Open cjuexuan opened 6 years ago
最近遇到一个内存泄漏的case,这里分享下,这是一个老业务,最近代码没改,只是增加了对一个稍大的hbase集群的监控(大概4w个region,之前已经接过另一个小的hbase集群,跑了很久也没问题),在我们上线之后很快运维就找到我们,说我们这台机器用到了swap分区了,首先我们的Xmx是4G,没有oom,但是系统的res显示用到了50多g
首先没oom的话,应该不是堆内存和direct buffer的泄漏,让运维dump了下,dump下的heap文件很小,不到1G,又打了个jstack,线程数也是比较正常的,那么应该是native的内存泄漏,这下就比较难排查了,网上google了下相关工具,google出的gperftools还可以,于是配置了下,然后打了个heap看了下,按照占比sort下
0.0 0.0% 100.0% 5729.3 97.5% 0x00007fc84acf7fe5 0.0 0.0% 100.0% 5732.5 97.5% Hashtable::new_entry 0.0 0.0% 100.0% 5729.3 97.5% JVM_InternString 0.0 0.0% 100.0% 5729.6 97.5% StringTable::basic_add 0.0 0.0% 100.0% 5729.7 97.5% StringTable::intern@a24780 0.0 0.0% 100.0% 5729.3 97.5% StringTable::intern@a24ca0 0.0 0.0% 100.0% 5745.5 97.7% AllocateHeap 5867.9 99.8% 99.8% 5867.9 99.8% os::malloc@9137e0
这里给出占比最高的部分,我们发现是StringTable的intern和hashtable的new_entry
这里String的intern推荐阅读美团的tech blog
此时我们又找了下jdk的issue,看有没有和我们类似的问题,果然找到了两个链接
hotspot-gc-use jdk8 bugs
我们在这个应用上用的g1 gc,一直没有出现full gc,看到这个issue之后,我们就切回了cms,果然内存不再持续增长
进一步分析,我们这是一个http爬取hbase jmx的case,用到的框架比较少,内部框架主要调度相关的,另外用到了apache http client和jackson,我们自己肯定没有调用String intern的代码,搜索了下相关的api, 发现jackson中有一个特性,JsonFactory.Feature.INTERN_FIELD_NAMES,我们刚好用map接返回的数据,里面有很多region的hashcode,都是一些动态的key,感觉就被坑了一记了
JsonFactory.Feature.INTERN_FIELD_NAMES
如果用map接收对象,并且有动态key,要disable INTERN_FIELD_NAMES,最好还是用对象接,不过我们这个case刚好不好用对象处理,而且被连环的问题触发导致了最终的结果
这里贴一下我们最终发在公司博客上的文章
文章里面不是讲字符串是在堆内存创建的,常量池保存的是引用。为啥这里使用intern分配的内存是在堆外呀?
背景
最近遇到一个内存泄漏的case,这里分享下,这是一个老业务,最近代码没改,只是增加了对一个稍大的hbase集群的监控(大概4w个region,之前已经接过另一个小的hbase集群,跑了很久也没问题),在我们上线之后很快运维就找到我们,说我们这台机器用到了swap分区了,首先我们的Xmx是4G,没有oom,但是系统的res显示用到了50多g
实验
首先没oom的话,应该不是堆内存和direct buffer的泄漏,让运维dump了下,dump下的heap文件很小,不到1G,又打了个jstack,线程数也是比较正常的,那么应该是native的内存泄漏,这下就比较难排查了,网上google了下相关工具,google出的gperftools还可以,于是配置了下,然后打了个heap看了下,按照占比sort下
这里给出占比最高的部分,我们发现是StringTable的intern和hashtable的new_entry
分析
这里String的intern推荐阅读美团的tech blog
此时我们又找了下jdk的issue,看有没有和我们类似的问题,果然找到了两个链接
hotspot-gc-use jdk8 bugs
我们在这个应用上用的g1 gc,一直没有出现full gc,看到这个issue之后,我们就切回了cms,果然内存不再持续增长
进一步分析,我们这是一个http爬取hbase jmx的case,用到的框架比较少,内部框架主要调度相关的,另外用到了apache http client和jackson,我们自己肯定没有调用String intern的代码,搜索了下相关的api, 发现jackson中有一个特性,
JsonFactory.Feature.INTERN_FIELD_NAMES
,我们刚好用map接返回的数据,里面有很多region的hashcode,都是一些动态的key,感觉就被坑了一记了总结
如果用map接收对象,并且有动态key,要disable INTERN_FIELD_NAMES,最好还是用对象接,不过我们这个case刚好不好用对象处理,而且被连环的问题触发导致了最终的结果