LeakCanary内存优化框架源码解析

161 阅读2分钟

内存泄漏(OOM)的几种情况

  1. 单例造成的内存泄漏
  • 如果传入的是Activity的context,当Activity退出时内存并不会被回收,因为该单例对象持有了Activity的引用。
  • 解决:传入Application的context,使单例的生命周期和应用程序的生命周期一样长。
  1. 非静态内部类创建静态实例造成的内存泄漏
  • 静态(static)实例mResource的生命周期和应用程序一样长,导致静态实例一直持有StaticLeakActivity的引用,导致StaticLeakActivity无法被内存回收。
  • 解决:把非静态内部类noneStaticClass改成静态内部类(加static修饰)。
  1. Handler造成内存泄漏
  • postDelayed发送延迟消息时,mLeakyHandler会将Message添加到消息队列Message Queue中,此时若调用Activity的finish(),由于延迟执行任务的Message仍存在于主线程中,会持有Activity的Handler引用,此时finish掉的Activity就不会被回收(Message持有Handler引用,Handler持有Activity引用,导致Activity无法被回收)。
  • 解决:
    1. 将Handler声明为静态的,这样Handler的生命周期就和Activity无关了。
    2. 通过弱引用的方式引入Activity。
  1. 线程造成的内存泄漏
    Android中的线程: AsyncTask,Thread的Runnable。
  • 解决:将AsyncTask和Runnable都定义成静态内部类

Leakcanary原理

4种引用类型

  1. 强引用(StrongReference):正常使用的对象就是强引用。当空间不足时GC也不会回收,会抛出OOM。
  2. 软引用(SoftReference):当内存空间不够时会回收软引用对象。
  3. 弱引用(WeakReference):GC一旦发现弱引用对象,不管内存是否足够都会回收。
  4. 虚引用

引用队列 ReferenceQueue

LeakCanary总结

  • 1中ActivityRefWatcher用于监听Activity的回收情况。
  • 2中把Activity的生命周期和ActivityRefWatcher相关联。

核心方法checkForLeak

  • findLeakingReference: 查找产生内存泄漏的引用。
    通过迭代器Iterator迭代查找key,key值相等时就是要查找的内存泄漏的对象。最后把找到的key添加到ArrayList集合keysFound中。
    findLeakTrace: 找到最短的泄漏内存的路径。
    gcroot: 不能被垃圾回收器回收的对象。LeakCanary中关注gcroot的两种类型(通过findPath): 1.静态的; 2.该对象被其他线程使用且其他线程正在运行未结束。
    getTotalRetainedSize()计算内存泄漏大小。

checkForLeak总结

主要包括findLeakingReferencefindLeakTrace两大方法。