内存泄漏(OOM)的几种情况
- 单例造成的内存泄漏
- 如果传入的是Activity的context,当Activity退出时内存并不会被回收,因为该单例对象持有了Activity的引用。
- 解决:传入Application的context,使单例的生命周期和应用程序的生命周期一样长。
- 非静态内部类创建静态实例造成的内存泄漏
- 静态(static)实例mResource的生命周期和应用程序一样长,导致静态实例一直持有StaticLeakActivity的引用,导致StaticLeakActivity无法被内存回收。
- 解决:把非静态内部类noneStaticClass改成静态内部类(加static修饰)。
- Handler造成内存泄漏
postDelayed发送延迟消息时,mLeakyHandler会将Message添加到消息队列Message Queue中,此时若调用Activity的finish(),由于延迟执行任务的Message仍存在于主线程中,会持有Activity的Handler引用,此时finish掉的Activity就不会被回收(Message持有Handler引用,Handler持有Activity引用,导致Activity无法被回收)。- 解决:
- 将Handler声明为静态的,这样Handler的生命周期就和Activity无关了。
- 通过弱引用的方式引入Activity。
- 线程造成的内存泄漏
Android中的线程: AsyncTask,Thread的Runnable。
- 解决:将AsyncTask和Runnable都定义成静态内部类
Leakcanary原理
4种引用类型
- 强引用(StrongReference):正常使用的对象就是强引用。当空间不足时GC也不会回收,会抛出OOM。
- 软引用(SoftReference):当内存空间不够时会回收软引用对象。
- 弱引用(WeakReference):GC一旦发现弱引用对象,不管内存是否足够都会回收。
- 虚引用
引用队列 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总结
主要包括findLeakingReference和findLeakTrace两大方法。