android asynchttpclient内存泄漏,诊断由 Apache HttpAsyncClient 引起的内存泄漏

异步 IO 的使用, 使得线程不再 block 在 IO 上面, 可以做更多的事情, 所以 Java 的 NIO 在很多地方都使用起来了. 同时由于微服务的广泛普及, 企业内部各种服务直接的相互调用更多了. 之前很多都是使用 Apache 社区的 HttpClient 来相互调用, 如今更多的代码转向了 HttpAsyncClient. 这里就记录一个由于 HttpAsyncClient 的错误使用引起的内存泄漏的案例.

某个应用发布新版本之后, 发现没过多久就 GC overhead 了, 查看 verbose GC log, 确认是 heap 用光之后, 就做了一个 heap dump, 从这个 MAT 分析的结果看, 这个泄漏貌似跟应用没有半毛钱关系:

ef850d8a2d8cfcfa0cbf1396674ec64d.png

就是说内存里面太多的 EPollArrayWrapper 和 SSLSessionContextImpl 对象了.8,393 instances of "sun.nio.ch.EPollArrayWrapper", loaded by "" occupy 559,669,328 (41.27%) bytes. 

Keywords
sun.nio.ch.EPollArrayWrapper

1,675 instances of "sun.security.ssl.SSLSessionContextImpl", loaded by "" occupy 440,123,744 (32.46%) bytes. 

Keywords
sun.security.ssl.SSLSessionContextImpl

从这 2 个对象看, 貌似跟 Java 的 NIO, 尤其和基于 HTTPS 的 NIO 有很大的关系. Java 的 NIO 使用的是单独的 IO 线程去处理 IO 的, 所以接着去看看 thread dump. 不过这里直接从 heap dump 也能看到, 看到非常多的 "I/O dispatcher" 线程, 如下图:

375444b08fc0c6805673d0146db7bbb7.png

并且从编号来看, 已经编号到 6617+ 了. 另外, 看到另外很多的线程池, 里面只有一个线程, 线程名字都是 "pool-xxx-thread-1", 说明创建了很多只有一个线程的线程池, 却没有重用, 也忘记关了, 一直新建.

使用 btrace 去观察线程的创建, 我们发现了问题所在:java.lang.Thread.init(Thread.java)

java.lang.Thread.init(Thread.java:349)

java.lang.Thread.(Thread.java:678)

java.util.concurrent.Executors$DefaultThreadFactory.newThread(Executors.java:613)

org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase.(CloseableHttpAsyncClientBase.java:58)

org.apache.http.impl.nio.client.InternalHttpAsyncClient.(InternalHttpAsyncClient.java:81)

org.apache.http.impl.nio.client.HttpAsyncClientBuilder.build(HttpAsyncClientBuilder.java:871)

原来是有人一直在创建 HttpAsyncClient, 导致不断的创建新的 http 处理线程和 IO 线程. 由于 IO 线程是和 server 的 CPU 个数相关的, 所以每当创建一个 HttpAsyncClient, 就会创建一个只有一个线程的线程池, 和 4 个 "I/O dispatcher" 线程(因为有 4 个 CPU). 由于没有人去 close 这个 closable 的 HttpAsyncClient, 所以它一直增长, 最终导致内存爆掉.

为什么会一直创建呢? 因为开发人员本想创建一个单例的 HttpAsyncClient, 在第一次 getInstance() 的时候, 检查是不是 null, 不是 null 就新建. 但是新建完之后, 么有赋值个 _instance 单例字段, 导致每次 getInstance() 都创建新的.

所以遇到这类问题, 如果看到内存是由很多的 EPollArrayWrapper 撑爆的, 那么可以先去检查一下 thread dump, 是不是有太多的 "I/O dispatcher" 线程. 另外如果是 Https 访问, 可能会出现很多的 SSLSessionContextImpl 对象.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值