美文网首页Android程序员Android开发
Android内存泄漏总结和leakcanary使用

Android内存泄漏总结和leakcanary使用

作者: 奔跑吧李博 | 来源:发表于2017-12-22 12:25 被阅读1713次
    什么是内存泄漏?

    Android虚拟机的垃圾回收采用的是根搜索算法。GC会从根节点(GC Roots)开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。但是当对象不再被应用程序使用,仍然被生命周期长的对象引用,垃圾回收器无法回收。
    内存泄露的根本原因:长生命周期的对象持有短生命周期的对象。短周期对象就无法及时释放。

    哪些情况会造成内存泄漏?
    • 错误使用单例造成的内存泄漏
    • Handler造成的内存泄漏
    • 线程造成的内存泄漏
    • 非静态内部类创建静态实例造成的内存泄漏
    • 资源未关闭造成的内存泄漏
    LeakCanary检测内存泄漏:

    LeakCanary是Square公司出的开源框架(Square出品,必属精品),是一个Android和Java的内存泄露傻瓜化并且可视化的内存泄露分析工具,当检测到某个activity有内存泄露,LeakCanary会弹出通知,定位到内存泄露的位置。使用它大大减少OOM的问题,提高App的质量。

    1. 在 build.gradle 中加入引用:
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
    
    1. 创建Application配置并使用Application:
    import android.app.Application;
    import android.content.Context;
    import com.squareup.leakcanary.LeakCanary;
    import com.squareup.leakcanary.RefWatcher;
    
    public class MyApplication extends Application{
        private RefWatcher refWatcher;
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            refWatcher = LeakCanary.install(this);
        }
    
        public static RefWatcher getRefWatcher(Context context){
            MyApplication application = (MyApplication)context.getApplicationContext();
            return application.refWatcher;
        }
    }
    
    1. 使用 RefWatcher 监控 Activity:
    public class TestActivity extends AppCompatActivity {
        private TextView textView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
    
            RefWatcher refWatcher = MyApplication.getRefWatcher(this);
            refWatcher.watch(this);
        }
    }
    
    模拟单例模式造成内存泄漏情景:
    public class SingletonManager {
        private Context mContext;
        private static SingletonManager instance;
    
        private SingletonManager(Context context){
            mContext = context;
        }
    
        public static synchronized SingletonManager getInstance(Context context){
            if(instance == null){
                instance = new SingletonManager(context);
            }
            return instance;
        }
    }
    

    稍微玩弄一下就内存泄露了:


    giphy.gif

    当传入Activity的this,给SingletonManager.getInstance(this),当这个 Context 所对应的 Activity 退出时,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity 退出时它的内存并不会被回收,这就造成泄漏了。

    修复方法:改成SingletonManager.getInstance(getApplicationContext())就可以了。

    模拟Handler造成的内存泄漏情景:

    Handler的生命周期和Activity可能不一致:当Activity销毁时,消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler是内部类,持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。

    public class TestActivity extends AppCompatActivity {
        private Handler handler = new Handler();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
    
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
    
                }
            },10000);
            finish();
        }
    }
    

    修复方法:

    1. 使用静态内部类,静态内部类就不存在外部类的引用
    2. 使用弱引用WeakReference
      项目中使用Handler可以写一个统一的Handler使用。
    import android.os.Handler;
    import android.os.Message;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import java.lang.ref.WeakReference;
    
    public class TestActivity extends AppCompatActivity {
        private Handler handler = new MyHandler(this);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
    
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
    
                }
            },10000);
            finish();
        }
    
        public static class MyHandler extends Handler{
            private WeakReference<TestActivity> mActivity;
    
            public MyHandler(TestActivity activity){
                mActivity = new WeakReference(activity);
            }
    
            @Override
            public void handleMessage(Message msg) {
                if (mActivity.get() == null) {
                    return;
                }
    
                //接受到消息的处理
            }
        }
    }
    
    线程造成的内存泄漏

    线程如果在Activity中使用匿名内部类,那么它对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏。该情况同样是要改成使用静态内部类方法。

    资源未关闭造成内存泄漏

    在使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源后,当Activity销毁时,要及时关闭这些资源,避免不必要的内存泄漏。

    相关文章

      网友评论

        本文标题:Android内存泄漏总结和leakcanary使用

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