当java应用出现内存溢出异常时,导出heapdump(堆内在dump文件)文件进行问题分析、定位是必不可少的步骤
一、导出heapdump文件方法
获取dump方式:在jvm运行用户下执行(在非JVM运行用户下执行可能会导致失败或者及其缓慢),以下是获取全量dump的命令
jmap -dump:format=b,file=cjds-rpc-provider-1016.hprof {jvm_pid}
二、dump文件分析工具
1、IMB mat(离线分析)
2、jvm自带jvisualvm(离线分析+在线监控)
3、Jprofile(商业软件)
三、案例分析
jvisualvm载入dump文件
图中可以看出dump生成的日期,以及JVM内存快照占用大小
文件大小: 1,049.8 MB ==>1.05G
字节总数: 1,014,822,217
类总数: 10,796 ==>1w
实例总数: 18,007,106 ==>18000w
类加载器: 1,221
垃圾回收根节点: 6,216 ==>6k
等待结束的暂挂对象数: 0
从基本信息中得知 整个文件大小将近1.05G,类实例总数18000w, 在访问量不大的情况下,生成如此之多的对象实例,这是及其不合理的,可能潜在问题:
1、内存泄露,对象使用后未释放,例如LocalThread, static 变量,或者某个类的属性持有对象
2、某个方法生成临时对象过多,例如一次次查询数据库表中所有记录
基于猜测点,我们下一步分析dump中类实例分布以及类实例内存占用分布
类实例总数分析
1. 类实例数,从图中看出String,char[],Date,Integer,Fans,bytes[] 实例数最多,其中String 占比34.7%, 程序中不会单独如此大批量生成String对象,可能在跟随某个对象生成,再者char[],Date,Integer,bytes[]也如此大量,Top10中有Fans类实例,可能是这个造成的,继续跟进,按占用内存大小排序
2. 类实例内存top,图中可见,Fans实例数为62w,我们数据的粉丝数共计30多w,到目前为止可以判定为查询粉丝数据时全量查询,如果查询多次,那么会创建多个30w长度数组大小的实例。
jvisualvm没有直观提供每个类所有实例占heap内存大小,切换至MAT(IBM工具, 类对象实例内存占比分析(切换MAT工具分析) ArrayList占用626M(75%),一个Fans列表占用312M(37.5%),可以确定是由于大量的Fans类实例造成的OOM(java heap space),从而导致dubbo从zookeeper中取消服务注册
好了到这里已经分析出当前heap中占用最大的内存的类是哪一个了
结论
cjds-rpc内存占用高的一个问题是Fans对象多,占用绝大部分heap空间,在内存低的环境下容易造成Java heap space溢出,建议在代码core层做代码走读,排查粉丝查询数据库的代码,如果多次触发特定条件时再多的内存也是不够用的