图片为证
图片上为问题代码
问题代码.jpg
1.代码解读
一句话解读:通过oss sdk获取oss私有图片临时链接
2.问题发现
-
日常巡检
- 发现sentinel后台服务实例pod有掉线情况
- k8s后台确认服务异常,超过设置弹性伸缩指标
-
阿里后台
- fullgc频繁(新服务上线后未关注)
- cpu、负载间断升高(fullgc引起)
-
项目重启后内存持续升高(途中几段都为重启后运行)
阿里后台发现1.jpg
3.解决过程
- 1.根据阿里后台现象判断有明显内存泄漏
- 2.找运维同学查看jvm指标,导出dump文件
查看堆中内存占用最大的对象,同时触发手动FullGC
jmap -histo:live <pid>|head
导出dump文件
jmap -dump:format=b,file=name.hprof <pid>
- 3.后让运维同学重启服务并升级服务配置:1c2g->2g4G,避免短期频繁触发fullgc、触发弹性伸缩
dump文件分析
我使用的是jprofiler破解版,导入即可查看离线jvm堆栈信息
-
1.jvm堆中实例数排序
实例数排序.jpg- 开始我一直在关注这个排序结果,最多的实例为HashMap$Node,是个反射对象
- 继续查看对象根实例,发现很凌乱,根里面什么信息都有,看不出哪里内存泄漏
- 根据上面的结果初步判定是代码中序列化出现了内存泄漏(关注的点和判断结果导致排查方向错误)
- 于是去排查了代码(不知道哪里的问题,尝试解决)
- 线程池核心线程配置不符合实际
- 发现fastjson和参考服务版本不一致,网上也有类似案例:序列化要配置,全局共享实例SerializeConfig.globalInstance
- restTemplate未指定连接池
- 调整线程池、升级fastjson版本、指定restTemplate连接池,处理后上线,观察后并未得到解决
-
反查了fastjson两个版本1.2.58,1.2.62,已经默认是全局共享实例了(改这个版本实际啥用也没有)
fastjson1.jpg
- ======================================================
- 前面所有尝试无效后,又去查代码,发现可疑代码片段(见开头)
- 经过优化ossclient为单例、测试,初步定位就是oss连接无限创建,没有关闭导致的内存泄漏
优化代码.jpg
- 引发了疑问:jprofiler为啥看不出是http连接池问题导致内存泄漏?下图是找同事eclipse MAT分析查看结果:直接指出了问题所在
eclipse1.jpg eclipse2.jpg -
2.jvm堆中保留对象排序
保留对象排序.jpg
- jprofiler点击【保留大小】排序,其实也很容易发现问题,常见对象一般先忽略,可见异常对象:PoolingHttpClientConnectionManager
- 点击【显示到GC根的路径】,PoolingHttpClientConnectionManager整个大对象被一个数组对象引用着的,根路径为:com.aliyun.oss.common.comm.IdleConnectionReaper
- IdleConnectionReaper里面有个【静态数组】对象,就是用来放连接对象的,问题代码刚好未关闭连接,导致无限增长
4.总结
- 1.内存泄漏本身不是特别难处理的问题,解决过程一般来说都是固定程序(当然,也会遇到排除特别难发现的)
- 2.熟悉相关分析工具能事半功倍,案例中我就是因为不熟悉工具导致盲目尝试过,但是盲目过程中也优化了部分代码
5.思考
本次内存泄漏是ossclient连接无限创建并未关闭导致,为什么从jprofiler看HashMap$Node实列数非常大?
网友评论