发现问题
早上客服接到反馈说app反应很慢。查看服务器日志发现app1号服务器发生了OOM。
还好之前在启动命令里加了遇到oom自动dumpheap的命令。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./appheapdump.hprof
先重启服务器,让服务可用。
开始分析
接下来下载heapdump文件appheapdump.hprof用MAT分析一下。

我一般会先看dominator tree

看起来最大的类也才占了1.86%。但是这只是默认的展示方式,没有分组的情况,我们来按照包名分组看看

可以看到 PoolingHttpClientConnectionManager 这个类创建了31万个对象,占据了92.92%的内存,引起OOM的罪魁祸首应该就是它了

我们打开IDEA 搜索这个类,可以看到引用的地方有两处,都指向了同一个类DefaultServiceClient

可以看到DefaultServiceClient是aliyun oss sdk中的类,有两个类(TimeoutServiceClient和OSSClient)引用了它。

查看TimeoutServiceClient。可以看到它实际上是继承了DefaultServiceClient。

最终还是OSSClient引用了它们,接下来我们只需要看OSSClient就好了。
可以看到这个构造方法是最终的构造方法,所有的构造方法最终都会调用它构造OSSClient。

随便找到一个使用OSSClint的地方。啧啧,没有使用单例而是随便new了一个OSSClient。并且没有调用shutdown方法。

可能写这行代码的同学会问,不是有gc吗?垃圾回收器会自动释放内存,为啥非得要手动shutdown呢?
我们回到最开始发现OOM的类PoolingHttpClientConnectionManager。
可以看到这是一个池化的对象。跟线程池,数据库连接池一样,创建Http请求是很消耗资源的,把这些资源池化可以减少创建资源的开销,使系统运行更快。但是如果无限创建线程池而不释放,就会导致OOM。
后续
开发的同学 用【OSSClient OOM】为关键字搜索了一下,发现GitHub有这样一个issue页面

可能最开始官方的demo里确实没有shutdown,导致开发的同学直接照抄了过来。
建议(来自chatGPT)
为了解决这个问题,开发同学可以通过以下方式来避免类似的问题:
- 定期检查代码中是否存在类似的内存泄漏问题,及时进行修复。
- 阅读官方文档和示例代码,了解每个 API 的使用方法和最佳实践。
- 使用内存分析工具,检测应用程序中的内存泄漏问题,并及时解决。
结论 (来自chatGPT)
通过内存分析工具的帮助,我们成功定位并解决了应用程序中的内存泄漏问题。在处理类似问题时,我们需要认真分析内存快照,并结合业务代码来找到潜在的问题所在。同时,我们也需要注意编写高质量的代码,并且定期检查和修复潜在的内存泄漏问题,以确保应用程序的稳定性和性能。
评论