Android内存泄漏
以下内容提取于Android进程的内存管理分析及Android内存泄漏分析及调试.
内存空间是一定的,所以在对象无用时就要回收一些对象来留出空间。当Java Garbage Collection开始运行时,它会从他了解还存活的对象作为内存遍历的根节点(GC Root),遍历heap内存空间,没有直接或间接引用到GC Root的对象便会被回收。
而Android内存泄漏便是指进程中的对象,虽然没有使用价值了,但它仍然有直接或间接的引用到GC Root,那么该对象便不会被GC回收,导致内存持续被占用,使可用内存变小。
常见的内存泄漏
查询数据库没有关闭Cursor。
使用BaseAdapter作为适配器时没有复用convertView。可以参考ListView与BaseAdapter优化.
bitmap没有回收,可以参考Bitmap相关:管理Bitmap内存.
注册对象后没有反注册,比如Broadcast Receiver等。
handler问题,如果handler是非静态的,会导致Activity或者Service不被回收,所以应当注册为静态内部类,同时在onDestroy时停止线程:
mThread.getLooper().quit();
Activity被静态引用,特别是缓存bitmap时,解决方法可以考虑使用Application的context代替Activity的context。
View在callback中被引用,可能回调还没有结束,但是view处于引用状态,无法回收
public void leak(final View view) { api.callback(new callBack() { onCallBack() { ... } ) }
WebView的泄露问题:在魅族上面发现webView打开再关闭就会内存泄露..目前使用的解决方法是在webview外面嵌套一层layout作为container.在Activity的
onDestroy
中调用container.removeAllViews()
方法.Dialog导致Window泄露,如果需要在dialog依附的Activity销毁前没有调用dialog.dismiss().会导致Activity泄露
JNI 中的内存泄露
除了 native code 的内存泄露, 即 C/C++ 中 new 出来的空间需要手动 free 之外, JNI 还需要特别考虑 LocalReference 和 GlobalReference 的泄露.
在 Java to native 的上下文切换期间, JVM 会分配一块区域用来存放 Local Ref, 每次 new 的对象都会存放在这个区域中, 这个区域只有在 native 执行完毕切回 Java 的时候才会清空, 如果这块区域满了, 就会报 OOM 错误.
而 Global Ref 则需要手动维护, 调用 DeleteGlobalRef 删除.
检测内存泄露的方法
Android Studio 自带的 Memory Monitor,手动触发GC可以看得比较直观,但是dump出来的文件需要处理才能用MAT打开,利用
sdk/platforms-tool/
下的 hprof-conv 文件,命令为:/hprof-conv source output
MAT这篇文章分析得比较透彻
Leakcanary,检测一些容易忽略的内存泄露很好用.
adb shell dumpsys meminfo [包名]
可以看到内存使用情况.
一些值得注意的地方
理论上来说,当你退出一个 Activity 后,强制GC一次,内存值应该回到跟进入 Activity 差不多的状态,否则就有可能是内存泄露了.但是有可能是图片加载库中对新 Activity 中加载的图片做了内存缓存,然而这部分内存 GC 可能暂时不会回收.
比如在应用中我使用了 Fresco 去加载本地图库中的图片, Fresco 对这些图片做了内存缓存,然而这部分的图片其实是不需要在内存中缓存的.