Android 内存泄漏工具使用

首先总结一下,平时编码过程需要注意的事项,避免OOM
我之前也有一篇文章介绍过:
Android内存溢出 内存泄漏
其它很详细的介绍文章也可以参考:
1. Android内存优化之OOM
2. Android应用开发性能优化完全分析

  1. 时刻记得不要加载过大的Bitmap对象;譬如对于类似图片加载我们要通过BitmapFactory.Options设置图片的一些采样比率和复用等,具体做法点我参考官方文档,不过过我们一般都用fresco或Glide开源库进行加载。
  2. 优化界面交互过程中频繁的内存使用;譬如在列表等操作中只加载可见区域的Bitmap、滑动时不加载、停止滑动后再开始加载。
  3. 有些地方避免使用强引用,替换为弱引用等操作。
  4. 对批量加载等操作进行缓存设计,譬如列表图片显示,Adapter的convertView缓存等。
  5. 尽可能的复用资源;譬如系统本身有很多字符串、颜色、图片、动画、样式以及简单布局等资源可供我们直接使用,我们自己也要尽量复用style等资源达到节约内存。
  6. 对于有缓存等存在的应用尽量实现onLowMemory()和onTrimMemory()方法。
  7. 尽量使用线程池替代多线程操作,这样可以节约内存及CPU占用率。
  8. 尽量管理好自己的Service、Thread等后台的生命周期,不要浪费内存占用。
  9. 尽可能的不要使用依赖注入,中看不中用。
  10. 尽量在做一些大内存分配等可疑内存操作时进行try catch操作,避免不必要的应用闪退。
  11. 尽量的优化自己的代码,减少冗余,进行编译打包等优化对齐处理,避免类加载时浪费内存。
  12. 关闭资源对象,比如数据库操作中得Cursor,IO操作的对象
  13. 调用了registerReceiver注册广播后,调用unregisterReceiver()来取消
  14. 调用了View.getViewTreeObserver().addOnXXXListener, 调用View.getViewTreeObserver().removeXXXListener
  15. 随时注意Context的使用是否会导致内存泄漏

现在开始看看,怎么来分析内存泄漏

LeakCanary

使用

参考《LeakCanary 中文使用说明》

在 build.gradle 中加入引用,不同的编译使用不同的引用:

 dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
 }

在 Application 中:

public class ExampleApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}

这样,就万事俱备了! 在 debug build 中,如果检测到某个 activity 有内存泄露,LeakCanary 就是自动地显示一个通知。

工作机制

  1. RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。
  2. 然后在后台线程检查引用是否被清除,如果没有,调用GC。
  3. 如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。
  4. 在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。
  5. 得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。
  6. HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。
  7. 引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。

demo

一个非常简单的 LeakCanary demo: https://github.com/liaohuqiu/leakcanary-demo 开始使用

public class TestActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        TextView textView = (TextView) findViewById(R.id.test_text_view);

        TestDataModel.getInstance().setRetainedTextView(textView);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
public class TestDataModel {

    private static TestDataModel sInstance;
    private TextView mRetainedTextView;

    public static TestDataModel getInstance() {
        if (sInstance == null) {
            sInstance = new TestDataModel();
        }
        return sInstance;
    }

    public void setRetainedTextView(TextView textView) {
        mRetainedTextView = textView;
    }
}

很明显,sInstance是静态的,然后持有textView,textview当然持有当前context,就是TestActivity了,所以内存泄漏
退出TestActivity之后,然后过个10s左右,然后出现一个通知

此图说明,sInstance引用了mRetainedTextView,mRetainedTextView引用了mContext,其实mContext就是TestActivity,所以最后泄漏了TestActivity这个instance,导致GC的时候不能被回收。

Eclipse MAT

参考:
1. 内存分析工具 MAT 的使用
2. Android 性能优化之使用MAT分析内存泄露问题

注意生成hprof文件之前,需要先手动的GC,然后再分析

GC_CONCURRENT 当你的堆内存快被用完的时候,就会触发这个GC回收
GC_FOR_MALLOC 堆内存已经满了,同时又要试图分配新的内存,所以系统要回收内存
GC_EXTERNAL_ALLOC 在Android3.0 (Honeycomb)以前,释放通过外部内存(比如在2.3以前,产生的Bitmap对象存储在Native Memory中)时产生。Android3.0和更高版本中不再有这种类型的内存分配了。
GC_EXPLICIT 调用System.gc时产生,上图中就是点击Cause GC按钮手动触发垃圾回收器产生的log信息

MAT中
Merge Shortest Paths to GC Roots 可以查看一个对象到RC Roots是否存在引用链相连接, 在JAVA中是通过可达性(Reachability Analysis)来判断对象是否存活,这个算法的基本思想是通过一系列的称谓”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走得路径称为引用链,当一个对象到GC Roots没有任何引用链相连则该对象被判定为可以被回收的对象,反之不能被回收,我们可以选择 exclude all phantom/weak/soft etc.references(排查虚引用/弱引用/软引用等)因为被虚引用/弱引用/软引用的对象可以直接被GC给回收.

AS中生成hprof文件


然后选择文件,点击右键转换成标准的hprof文件,就可以在MAT中打开了。
在使用Eclipse或者AndroidStudio抓内存之前,一定要手动点击 Initiate GC按钮手动触发GC


第二个按钮就是GC按钮,第三个就是生成AS可查看的hprof文件按钮

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值