内存泄漏方案比较

1,822 阅读3分钟
LeakCanaryMatrixKoomLiko(未开源)
检测方案监听 onDestroy监听 onDestroy阈值检测法监听 onDestroy
是否支持线上检测不支持不支持支持支持
检测内容Activity、FragmentActivity、Fragment、BitmapActivity、Fragment、Bitmap、ByteArray、StringActivity、Fragment
分析库SharkhahaSharkMAT

1、检测方案对比

Matrix 对 LeakCanary 的讨伐

  • VM 并没有提供强制触发 GC 的 API,通过 System.gc() 或 Runtime.getRuntime().gc() 只能“建议” 系统进行 GC,如果系统忽略了我们的 GC 请求,可回收的对象就不会被加入 ReferenceQueue
  • 将可回收对象加入 ReferenceQueue 需要等待一段时间,LeakCanary 采用延时 100ms的做法加以规避,但似乎并不绝对管用
  • 监测逻辑是异步的,如果判断 Activity 是否可回收时某个 Activity 正好还被某个方法的局部变量持有,就会引起误判
  • 若反复进入泄漏的 Activity,LeakCanary 会重复提示该 Activity 已泄漏

Matrix 对 Koom 的讨伐:

  • 自动且较为准确地监测 Activity 泄漏,发现泄漏之后再触发 Dump Hprof 而不是根据预先设定的内存占用阈值盲目触发

Matrix 做了如下的改进:

  • 增加一个一定能被回收的“哨兵”对象,用来确认系统确实进行了 GC
  • 直接通过 WeakReference.get() 来判断对象是否已被回收,避免因延迟导致误判
  • 若发现某个Activity无法被回收,再重复判断3次,且要求从该Activity被记录起有2个以上的Activity被创建才认为是泄漏,以防在判断时该Activity被局部变量持有导致误判
  • 对已判断为泄漏的 Activity,记录其类名,避免重复提示该 Activity 已泄漏

2、线上检测方案

目前的 leakCanary 和 Matrix 都是在主线程做的 dump hprof ,由于 dump 会挂起所有 java 线程,导致主线程阻塞,对于用户来说是万万不能接受的,Koom 和 Liko 采用 linux 的 copy-on-write 机制,从当前的主线程 fork 出一个子进程,然后在子进程进行 dump 分析,对于用户所在的主线程不会有任何感知。

3、内存泄漏兜底

在检查到内存泄漏的 Activity 或是 Fragment 时,可以主动释放泄漏的 Activity 持有的 ViewTree 的背景图和 ImageView 图片

Drawable d = iv.getDrawable();
   if (d != null) {
       d.setCallback(null);
   }        
   iv.setImageDrawable(null);

KOOM 无法做内存泄漏兜底,由于它是阈值检测法,已经拿不到 Activity 实例去遍历兜底了,其他几种基于 onDestroy 的方案都可以

4、总结

都别吵吵,这里面字节的 liko 方案是最好的,他不仅吸收了 matrix 对泄漏的检测优点,也吸收了 koom fork dumo 的优点,方便应用于线上,缺点就是,没开源。如果想改造尝试的话,可以在 Matrix 的基础上,将 Koom 的 form dump 部分拷贝过去。造轮子就是吸取精华,弃其糟粕。

参考文档: