欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android内存泄漏记录

程序员文章站 2022-03-21 07:49:18
android内存泄漏点记录常见的四种情况1. 将context或者view置为static(view会默认持有一个context的引用,置为static的话会造成view在方法区中无法快速被回收,从而导致activity的泄漏)public class TestActivity extends AppCompatActivity { private static ImageView imageView; @Override protected void onCreate(Bund...

android内存泄漏点记录

常见的四种情况

1. 将context或者view置为static(view会默认持有一个context的引用,置为static的话会造成view在方法区中无法快速被回收,从而导致activity的泄漏)

public class TestActivity extends AppCompatActivity {

    private static ImageView imageView;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        imageView = findViewById(R.id.test_img);
        imageView.setImageResource(R.drawable.ic_launcher_background);
    }
}

上述的代码中的imageView会导致TestActivity无法被GC回收

2. 常见的未解注册的各种listener(如广播,Observer的监听等)

3. 非静态Handler导致泄漏

    //------------------------注释1-------------------------------
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

    //------------------------注释2-------------------------------
    private static class MyHandler extends Handler {
        private final WeakReference<TestActivity> mActivity;
        public MyHandler(TestActivity activity) {
            mActivity = new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(@NonNull Message msg) {
            TestActivity testActivity = mActivity.get();
            if (testActivity != null) {
                // do something
            }
        }
    }

上述代码中注释1是日常开发中写的样子也会造成Activity的内存泄漏,一般需要将其置为static,然后内部持有一个Activity的弱引用来避免内存泄漏。如注释2所示。

4.三方库使用的Context

三方库初始化的时候一般使用applicationContext作为上下文参数,一般如果我们自己开发相关的SDK的时候一般用context.getApplicationContext() 的方法避免内存泄漏

LeakCanary的使用及原理分析

原理:

​ LeakCanary 中对内存泄漏检测的核心原理就是基于 WeakReference 和 ReferenceQueue 实现的。

  1. 当一个 Activity 需要被回收时,就将其包装到一个 WeakReference 中,并且在 WeakReference 的构造器中传入自定义的 ReferenceQueue。

  2. 然后给包装后的 WeakReference 做一个标记 Key,并且在一个强引用 Set 中添加相应的 Key 记录

  3. 最后主动触发 GC,遍历自定义 ReferenceQueue 中所有的记录,并根据获取的 Reference 对象将 Set 中的记录也删除

  4. 经过上面 3 步之后,还保留在 Set 中的就是:应当被 GC 回收,但是实际还保留在内存中的对象,也就是发生泄漏了的对象。

主要触发

LeakCanary 中监听 Activity 生命周期是由 ActivityRefWatch 来负责的,主要是通过注册 Android 系统提供的 ActivityLifecycleCallbacks,来监听 Activity 的生命周期方法的调用

 new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
            }
            @Override
            public void onActivityStarted(@NonNull Activity activity) {
            }
            @Override
            public void onActivityResumed(@NonNull Activity activity) {
            }
            @Override
            public void onActivityPaused(@NonNull Activity activity) {
            }
            @Override
            public void onActivityStopped(@NonNull Activity activity) {
            }
            @Override
            public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
            }
            @Override
            public void onActivityDestroyed(@NonNull Activity activity) {
            }
        };

当监听到 Activity 的 onDestroy 方法后,会将其传给 RefWatcher 的 watch 方法

核心类 RefWatcher

  1. 在其watch()方法中,主要操作

    1. 生成一个随机的字符串 key,key 就是用来标识 WeakReference 的,就相当于给 WeakReference 打了个标签
    2. 将被检测对象包装到一个 WeakReference 中,并将其标识为步骤 1 中生成 key
    3. 调用 ensureGoneAsync 开始执行检测操作
  2. ensureGoneAsync 方法操作,其中实现了内存泄漏的检测

    1. 遍历 ReferenceQueue 中所有的元素,并根据每个元素中的 key,相应的将集合 retainedKeys 中的元素也删除

    2. 判断集合 retainedKeys 是否还包含被检测对象的弱引用,包含说明被检测对象并没有被回收,发生了内存泄漏

    3. 生成 Heap “堆”信息,并生成内存泄漏的分析报告

检测时机 为了尽量不影响 UI 线程的渲染,LeakCanary 也做了些优化操作

向主线程 MessageQueue 中插入了一个 IdleHandler,IdleHandler 只会在主线程空闲时才会被 Looper 从队列中取出并执行。通过 addIdleHandler 也经常用来做 App 的启动优化,比如在 Application 的 onCreate 方法中经常做 3 方库的初始化工作。可以将优先级较低、暂时使用不到的 3 方库的初始化操作放到 IdleHandler 中,从而加快 Application 的启动过程

//getMainLooper().myQueue()或者Looper.myQueue()
Looper.myQueue().addIdleHandler(new IdleHandler() {  
    @Override  
    public boolean queueIdle() {  
        //你要处理的事情
        return false;    
    }  
});

本文地址:https://blog.csdn.net/ZKhero/article/details/107150905