文/@寒江不钓(贾吉鑫)
概念
根搜索算法
Android 虚拟机的垃圾回收采用的是根搜索算法
。GC 会从根节点(GC Roots)开始对 heap 进行遍历。到最后,部分没有直接或者间接引用到 GC Roots 的就是需要回收的垃圾,会被 GC 回收掉。
根搜索算法相比引用计数法很好的解决了循环引用的问题。举个例子,Activity 有 View 的引用,View 也有 Activity 的引用,之前我还尝试去源代码里找 Activity 何时和 View 断开连接是大错特错了。当 Activity finish 掉之后,Activity 和 View 的循环引用已成孤岛,不再引用到 GC Roots,无需断开也会被回收掉。
内存泄漏
Android 内存泄漏
指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到 gc roots 导致无法被 GC 回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了。
场景
Handler
临时性内存泄露预防
context-application
代替context-activity
WeakReference
代替。检测
静态检测
静态检测主要是检测资源未关闭的情况,Eclipse 和 Android Studio 都可以检测出 IO 或者 Socket 未关闭的情况,然后在 finally 中关闭即可。
动态监测
动态检测主要是依靠 MAT 这个工具。2011 年 Google IO 有一个主题演讲,非常详细地讲解了内存泄露的检测,包含 MAT 工具的使用,值得一看。我在某项目中使用 MAT 检测,发现一处内存泄漏,分享一下过程。从首页到商户列表到商户详情再退回首页执行Dump HPROF File
,查看 MAT 中的Histogram
,过滤 Activity 后,结果如下:
Histogram
仍然存在ShopInfoActivity
的实例,选中右键点击Merge Shortest Paths to GC Roots
,结果如下:
此处输入图片的描述
可以看到ShopDatabase
中维持着ShopInfoActivity
的引用,查看源代码如下:
public class ShopDatabase { … private static ShopDatabase instance; public static ShopDatabase getInstance (Context context) { if (instance == null && context != null) { instance = new ShopDatabase (context); } return instance; } protected Context context; … }
很明显,静态变量instance
长期持有context
的引用,造成内存泄露。
所以动态检测内存泄露的一个简单思路就是随意操作 APP,最后返回首页,然后用 MAT 检测,查看是否存在 Activity 多于一个或者 Activity 不正常存在的问题。