内存问题阅读笔记

167 阅读2分钟

包括但不限于

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的强引用场景