美文网首页Android日记基本知识Android 技术开发
总结工作中的Android内存泄漏问题

总结工作中的Android内存泄漏问题

作者: InKenKa | 来源:发表于2017-01-12 21:02 被阅读464次

    简单判断是否有内存泄漏

    判断内存泄漏的定位的大单位是Activity。

    可以通过反复进入退出一个Activity,然后用adb shell dumpsys meminfo + 包名 查看虚拟机的堆是否有不断地增长


    定位内存泄漏

    1.使用Leak Canary

    在代码上加入Leak Canary,然后不断跑Monkey或者手动反复进出不同页面。若出现内存泄漏问题,会自动导出来,生成以下页面。

    内存泄漏的引用关系图

    2.使用DDMS导出hprof,并用MAT工具进行分析

    0)强烈建议先跑30分钟Monkey测试

    1)使用eclipse的ddms找到对应的进程,触发一次gc后,dump出里面的内存快照hprof文件以分析当前应用内存的堆有什么东西

    2)使用Android SDK 里的platform-tools文件夹的 hprof-conv工具,对刚才 hprof 文件进行转换,以至于 后面MAT工具能正常打开

    3)使用MAT打开hprof文件,进入Histogram。输入自己猜测可能泄漏的Activity(项目中Activity不多时,可每个Activity都重复以下3、4、5步骤)

    在MAT中搜索BrowserActiviy

    4)右键该其中一项,打开菜单选择list objects ->with incoming refs将列出该类的实例

    下图展示了对象间的引用关系。

    BrowserActivity在内存中的引用关系

    5)右健Path to GC Roots-->exclue all phantom/weak/soft etc. reference,找出这个实例GC后,还会存在什么对象的引用关系。

    下图的情况是:NetworkChangeNotifier类导致BrowserActivity导致内存泄漏

    导致BrowserActivity内存泄漏的引用关系图

    常见导致内存泄漏的几个点

    生命周期的原因

    比如:Activity中关联了一个生命周期超过Activity的Thread,这个Thread 若持有该Activity的引用,就会导致内存泄漏。

    内部类的原因

    因为内部类会隐式地持有外部类的引用,若内部类不被释放,外部类也是无法释放。常见的有内部的Listener、Callback、Handler等导致。

    情景1:若外部类应该释放的时候,内部类还在执行里面的函数,会导致外部类无法释放。

    情景2:若一个异步操作,会回调内部类的Listener、Callback、Handler。当外部类应该释放的时候,但是这个异步操作还存在,而这个异步操作类又持有了Listener、Callback、Handler,导致外部类无法被释放。PS:这个原因也属于生命周期的原因。

    静态变量的原因

    单例类里包含Activity

    静态变量的类里引用到Activity

    注册与反注册、打开与关闭没成对出现的原因

    比如:注册广播接收器、注册观察者(典型的譬如数据库的监听)等。或者自己写的跟Activity引用有关的clear()函数没有成对出现


    解决方法

    1)解决内部类的问题(以Handler作为例子)

    1. onDestroy时候remove所有msgActivity finish后未处理的msg是问题根源,所以清空所有未被执行的msg
    mHandler.removeCallbacksAndMessages(null);
    

    PS:比如Listener、Callback等其他内部类的问题,页面退出的时候,应该完成必要的清理操作,比如Cancel 请求

    1. 使用静态内部类 + weakReference
      静态内部类不会保留对外部类的引用,如果一定要引用外部类,使用weakReference
        static class MyHandler extends Handler {
            WeakReference<Activity > mActivityReference;
            MyHandler(Activity activity) {
                mActivityReference= new WeakReference<Activity>(activity);
            }
        
            @Override
            public void handleMessage(Message msg) {
                final Activity activity = mActivityReference.get();
                if (activity != null) {
                    mImageView.setImageBitmap(mBitmap);
                }
            }
        }
    

    PS:比如Listener、Callback等其他内部类的问题,也可以通过这个方法来解决

    2)单例类里面尽量不要传入Activity,最好穿入ApplicationContext。假如传入了Activity,持有的时长也不能大于Activity的生命周期

    3)对象的注册与反注册要成对出现

    4)不使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存

    5)因为View会持有Context,所以注意不要异步引用View,不要让静态对象持有View,不要在集合框架中存储View

    相关文章

      网友评论

        本文标题:总结工作中的Android内存泄漏问题

        本文链接:https://www.haomeiwen.com/subject/kyfjbttx.html