从LeakCanary看如何判断对象被回收

前面已经了解了Service,Fragment,ViewModel对象的销毁时机,那么在触发销毁时机后,我们怎么判断这些对象有没有回收呢?

大家都知道在Java中有强引用,弱引用,软引用,虚引用四种引用方式,而我们判断对象是否回收,就需要通过弱引用来实现,针对弱引用而言。其提供了两种构造方法,如下图所示:

从LeakCanary看如何判断对象被回收_第1张图片

其中我们重点需要关注第二个构造函数,从函数说明可以看出当该弱引用对象创建后,如果该弱引用所引用的对象被GC,则该弱引用对象会被放入给定的ReferenceQueue中,以便系统回收(需注意WeakReference对象和WeakReference引用的对象的区别,前者是WeakReference类的实例,后者是弱引用对象实例,弱引用对象实例在WeakReference的构造函数中传入),这也就意味着我们可以将应该被销毁的对象收集起来,并为他们依此指定ReferenceQueue,通过比对ReferenceQueue中对象情况和我们收集到的对象情况来判断该对象是否被正常销毁。

WeakReference标志化

从LeakCanary看如何判断对象被回收_第2张图片

我们可以看到WeakReference本身是范型对象,这种情况下为了存储多种对象,我们通常会考虑范型T直接指定为Object类型,这也就导致指定WeakReference没办法和其他WeakReference区分,进而进行比较,此时就要求我们要为WeakReference生成唯一标识。

这里我们通过自定义WeakReference来实现,为每一个WeakReference对象赋予一个唯一标识mKey,代码如下:

 public class KeyedWeakReference extends WeakReference {
     private String mKey;
     
 public KeyedWeakReference(String key, Object referent, ReferenceQueue q) {
     super(referent, q);
     mKey = key;
 }
 
 public String getKey() {
     return mKey;
 }
 
 @NonNull
 @Override
 public String toString() {
     return "KeyedWeakReference{ mKey=" + mKey + ",Object=" + get() + " }";
 }
} 

监听对象回收

当某一对象需要回收时,首先我们将该对象包装在WeakHashMap中,以UUID为key,以WeakReference对象为value,随后将该对象的ReferenceQueue指定为我们自定义的,在一段时间后遍历ReferenceQueue,将Queue中存在的所有WeakReference对象按照key从WeakHashMap中移除,HashMap中剩下的就是有可能发生了内存泄漏的对象。

详细的实现代码如下:

 public class ObjectWatcher {
     private static final String TAG = "ObjectWatcher";
     private ReferenceQueue mReferenceQueue;
     private WeakHashMap mReferences;
     
 private ObjectWatcher() {
     mReferenceQueue = new ReferenceQueue();
     mReferences = new WeakHashMap<>();
 }
 
 private static volatile ObjectWatcher mInstance;
 public static ObjectWatcher getInstance() {
     if (mInstance == null) {
         synchronized (ObjectWatcher.class) {
             if (mInstance == null) {
                 mInstance = new ObjectWatcher();
             }
         }
     }
     return mInstance;
 }
 
 public void watch(Object object) {
     String key = UUID.randomUUID().toString();
     Log.d(TAG, "watch object:" + object + ",key:" + key);
     KeyedWeakReference weakReference = new KeyedWeakReference(key, object, mReferenceQueue);
     mReferences.put(key, weakReference);
     Handler mainHandler = new Handler(Looper.getMainLooper());
     mainHandler.postDelayed(new Runnable() {
         @Override
         public void run() {
 
             KeyedWeakReference keyedWeakReference = null;
             do {
                 keyedWeakReference = (KeyedWeakReference) mReferenceQueue.poll();
                 Log.d(TAG, "keyedWeakReference:" + keyedWeakReference);
                 if (keyedWeakReference != null) {
                     mReferences.remove(keyedWeakReference.getKey());
                     Log.d(TAG, "object has been destroyed:" + keyedWeakReference.toString());
                 }
             } while (keyedWeakReference != null);
         }
     }, 5000);
 }
}
 
  

在上述代码中,我们将需要观察的对象通过watch方法传入,随后创建KeyedWeakReference对象,分别将该对象装入ReferenceQueue(系统底层代码实现)和mReferences WeakHashMap中,随后在5秒后遍历ReferenceQueue,确实监听到了对象被回收,日志打印如下:

从LeakCanary看如何判断对象被回收_第3张图片

结合上文我们就可以判断一个对象是否已经被回收了,当然针对WeakHashMap中仍然存在的对象,我们可以触发一次GC后,再次遍历观察。

为什么是弱引用,相信有熟悉四大引用的朋友,也看到过软引用和虚引用的构造函数,这两种引用的构造函数也可以指定ReferenceQueue,如下图所示:

从LeakCanary看如何判断对象被回收_第4张图片

从LeakCanary看如何判断对象被回收_第5张图片

那么为什么不使用软引用或者虚引用,非要使用弱引用呢?Github issue上也有同样的疑问,如下图:

从LeakCanary看如何判断对象被回收_第6张图片

从图中可以看到,这里主要的考虑应该是触发的频次,对于弱引用而言,其在下次GC时就会触发。

Android 学习笔录

Android 性能优化篇:https://qr18.cn/FVlo89
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap

你可能感兴趣的:(移动开发,Android,LeakCanary,android,笔记,移动开发,LeakCanary)