arthas 定位内存泄露 - com.aliyun.oss.OSSClient 线程池问题

梦康 2022-02-22 22:07:10 2184

自从用了 arthas 这个工具,终于在 java 开发中体会到了 php 的 1/5 的快乐了。

前几日晚上开会途中,收到报警信息,提示某个 服务内存占用过高。

dump 一台机器的堆内存分析了下,发现堆内存里占用内存最多的实例对象是 org.apache.http.impl.conn.PoolingHttpClientConnectionManager,既然实例数一直上涨,肯定有不停的初始化调用的地方,直接使用arthas stack 查看这个对象的调用栈
image.png

查看源码发现在com.aliyun.oss.common.comm.DefaultServiceClient里初始化客户端的时候,会先创建一个连接池,然后将连接池其放入到一个守护线程 IdleConnectionReaper 里面进行连接的回收管理

image.png

空闲连接回收线程,会将每次创建的 OSS 客户端对应的 org.apache.http.impl.conn.PoolingHttpClientConnectionManager 放入 ArrayList
image.png
不主动关闭客户端com.aliyun.oss.OSSClient#shutdown的情况是不会调用removeConnectionManager方法的。

如果每次初始化之后都要com.aliyun.oss.OSSClient#shutdown,则违背了设计连接池的初衷。

小结

  1. 所以最好是使用单例模式,之所以设计 ArrayList来追加org.apache.http.impl.conn.PoolingHttpClientConnectionManager ,应该是考虑存在多个不同的OSSClient,比如多个账号或者多个oss地址。
  2. 如果下次遇到类似情况,我可能会优先尝试jmap -histo $pid|more 的方式看看有没有比较明显泄露的实例对象,因为这样操作比 dump 堆内存下来,离线分析要来的快;当然另一方面也不够直观。
  3. 自从用了 arthas 这个工具,终于在 java 开发中体会到了 php 的 1/5 的快乐了。