LeakCanary | Matrix | Koom | Liko(未开源) | |
---|---|---|---|---|
检测方案 | 监听 onDestroy | 监听 onDestroy | 阈值检测法 | 监听 onDestroy |
是否支持线上检测 | 不支持 | 不支持 | 支持 | 支持 |
检测内容 | Activity、Fragment | Activity、Fragment、Bitmap | Activity、Fragment、Bitmap、ByteArray、String | Activity、Fragment |
分析库 | Shark | haha | Shark | MAT |
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 部分拷贝过去。造轮子就是吸取精华,弃其糟粕。
参考文档: