包括但不限于
1、Activity泄漏
2、Cursor未关闭
3、过度使用线程
4、过度创建缓存
5、so库慢慢泄漏
6、webview内核泄漏
一、Activity泄漏
- Activity被某个生命周期远长与自己的对象直接或间接把持了。导致Activity在长时间无法被GC机制回收。
- 由于Activity是用户与App交互的承载者,也提供了与系统交互的Context,很有机会被把持的。而且泄漏后会牵连非常多的对象也泄漏。
- 爆发之前仅仅使App内存占用变大,故征兆不明显。所以需要在测试阶段主动检测、排查。
早期 人力
自动化测试阶段,内存占用达到阈值后,自动触发Hprof Dump。存档Hprof并发给人工通过MAT分析。后随着新业务代码越来越多,已力不从心。
中期 LeakCanary
- 检测结果可读性非常好
- 对排查出的问题,展示开源社区维护的解决方案
- 可以完全代替人力检测、分析Activity泄漏
- 检测、分析绑在一块,不适合分工合作、批量的自动化测试
后期 Plus版的LeakCanary->ResourceCanary
- 自动化测试部分负责的检测:检测并生成Hprof和必要的附加信息
- 监控平台服务端离线完成的分析:处理检测结果,生成引发泄漏的强引用链
- 裁剪Hprof:高价值信息是其中类和对象的描述,及描述所需的字符串信息。约占原数据的1/10。大大降低了后台传输、保存它的开销
案例
- 匿名内部类,隐式持有,外部类的引用,导致的泄漏
// 第一个类
public class ChattingUI extend MMActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chatting_ui);
EventCenter.addEventIListener(new IListener<IEvent>(){
// 这个IListener内部类里,有个隐藏成员this$,持有了外部成员ChattingUI
@Override
public void onEvent(){
// ...
}
});
}
}
// 另一个类
public class EventCenter{
// 此处ArrayList实例的生命周期,为App的生命周期
private static List<IListener> sListener = new ArrayList();
// sListener添加
public void addEvenIListener(IListener cb){
// ArrayList对象持有cb,cb.this$持有ChattingUI,导致ChattingUI泄漏
sListener.add(cb);
}
}
- 反注册函数,未按预期被调用
- 系统组件导致的Activity泄漏(如Sensormanager、InputMethodmanager)
- 耗时的Runnable持有Activity,或前面有耗时Runnable堵塞线程,导致持有Activity的Runnable一直没有机会移除
- 其他长期持有Activity的强引用场景