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

news2024/11/23 7:04:33

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

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

image-20230813162203745

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

WeakReference标志化

image-20230813163229106

我们可以看到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<String, KeyedWeakReference> mReferences;

    private ObjectWatcher() {
        mReferenceQueue = new ReferenceQueue<Object>();
        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,确实监听到了对象被回收,日志打印如下:

image-20230813164420870

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

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

image-20230813201912910

image-20230813201953656

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

image-20230813202511693

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/902936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

难缠客户背后的秘密:项目经理的危机处理手册

难缠的客户&#xff0c;每个项目经理都会或多或少遇到。他们可能会频繁更改需求&#xff0c;对细节吹毛求疵&#xff0c;或者在关键时刻提出与之前完全不同的意见。但是&#xff0c;难缠的客户其实并不都是坏事&#xff0c;他们也为项目经理提供了宝贵的学习机会。本文将深入探…

git拉取失败/git fatal终极解决方法

前言 被折磨不下20次总结出来的终极方案 步骤 0 首先关闭代理试试&#xff0c;不行就下一步 1 重置代理或者取消代理的方式 git config --global --unset http.proxy git config --global --unset https.proxy添加全局代理 git config --global http.proxy git config …

虚拟化 VMware sphere

一 VMware sphere用途&#xff1a; VMware vSphere 是 VMware 的虚拟化平台&#xff0c;可将数据中心转换为包括 CPU、存储和网络资源的聚合计算基础架构。vSphere 将这些基础架构作为一个统一的运行环境进行管理. 1. **虚拟化&#xff1a;** vSphere 的主要用途是将物理服务…

打印X型的图案

int main() {int n0;int i0;int j0;scanf("%d",&n);for(i0;i<n;i){for(j0;j<n;j){if(ij){printf("*");}else if((ij)n-1){printf("*");}elseprintf(" ");}printf("\n");}return 0; }

mapper.xml中循环执行多条语句时报错,但是单独拿SQL到数据库却可以执行

我是批量修改数据&#xff0c;用foreach标签包住update语句&#xff0c;报错信息如下&#xff1a; nested exception is java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the …

HCIP的VLAN实验

实验步骤&#xff1a; 1.首先&#xff0c;对交换机SW1进行操作&#xff0c;创建需要的VLAN并对接口进行划分 [SW1]vlan batch 2 to 6 [SW1]port-group group-member Ethernet 0/0/2 Ethernet 0/0/4 [SW1-port-group]port link-type access [SW1-port-group]port default vlan …

聚焦电力行业CentOS迁移,麒麟信安受邀参加第六届电力信息通信新技术大会暨数字化发展论坛并发表主题演讲

为加快推进“双碳”目标下的新型能源体系和新型电力系统建设&#xff0c;深化新一代数字技术与电力业务的融合发展&#xff0c;促进电力行业关键技术自主创新、安全可控&#xff0c;助力电力企业数字化转型升级和高质量发展&#xff0c;2023年8月9-11日&#xff0c;第六届电力信…

使用mysql:5.6和 owncloud 镜像,构建一个个人网盘。

一.根据自己版本选择镜像 uname -a cat /etc/centos-releaseuname -a 命令用于查看当前系统的硬件和操作系统信息&#xff0c;包括内核版本、处理器架构、系统类型等。 其中&#xff0c;"Linux" 表示操作系统类型为 Linux&#xff0c;"3.10.0-1160.el7.x86_64…

C++ 网络编程项目fastDFS分布式文件系统(四)-fastCGI项目相关技术以及linux搜狗输入法相关问题。

目录 1. Nginx作为web服务器处理请求 2. http协议复习 Get方式提交数据 Post方式提交数据 3. fastCGI 3.1 CGI 3.2 fastCGI 3.3 fastCGI和spawn-fcgi安装 1. 安装fastCGI 2. 安装spawn-fcgi 3.4 nginx && fastcgi 4其他知识点 1. fastCGI环境变量 - fas…

week5刷题

题解: 斐波那契数的边界条件是 F(0)0和 F(1)1。当 n>1 时&#xff0c;每一项的和都等于前两项的和&#xff0c;因此有如下递推关系&#xff1a; F(n)F(n−1)F(n−2) 由于斐波那契数存在递推关系&#xff0c;因此可以使用动态规划求解。动态规划的状态转移方程即为上述递推…

vector的迭代器失效问题

vector的迭代器存在一定隐患&#xff0c;以下几种方式会导致其迭代器失效 resize、reserve、insert、assign、push_back。 1.push_back导致迭代器失效 示例代码&#xff1a; #include<vector> #include <iostream> using std::cout; using std::endl; using std:…

PyTorch学习笔记(十六)——利用GPU训练

一、方式一 网络模型、损失函数、数据&#xff08;包括输入、标注&#xff09; 找到以上三种变量&#xff0c;调用它们的.cuda()&#xff0c;再返回即可 if torch.cuda.is_available():mynn mynn.cuda() if torch.cuda.is_available():loss_function loss_function.cuda(…

无涯教程-PHP - 预定义变量

PHP为它运行的脚本提供了预定义变量数组&#xff0c;其中包含来自Web服务器&#xff0c;环境和用户输入的变量。这些新数组称为超全局变量- PHP超全局变量 Sr.NoVariable & Description1 $GLOBALS 全局变量数组。 2 $_SERVER 存放提交过来的web路径、域名、来源、IP及各…

SQL助你面大厂(窗口函数)

在面试过程中窗口函数的应用可谓是数不胜数&#xff0c;前提你要知道什么是窗口函数&#xff0c;最常用的窗口函数有哪些&#xff1f;语法是什么&#xff1f;分别用的场景是什么&#xff1f;今天会以这三个问题开始我们今天的学习 什么是窗口函数&#xff1f; 所谓的窗口函数就…

C语言知识

C语言知识 链接 C语言中的数组初始化是有三种形式的&#xff0c;分别是&#xff1a; (1)数据类型 数组名称[长度n] {元素1,元素2…元素n}; (2)数据类型 数组名称[] {元素1,元素2…元素n}; (3)数据类型 数组名称[长度n]; 数组名称[0] 元素1; 数组名称[1] 元素2; 数组…

【LeetCode】2236.判断根节点是否等于子节点之和

题目 给你一个 二叉树 的根结点 root&#xff0c;该二叉树由恰好 3 个结点组成&#xff1a;根结点、左子结点和右子结点。 如果根结点值等于两个子结点值之和&#xff0c;返回 true &#xff0c;否则返回 false 。 示例 1&#xff1a; 输入&#xff1a;root [10,4,6] 输出&…

(牛客网)链表相加(二)

嗯哼~ 题目 描述 假设链表中每一个节点的值都在 0 - 9 之间&#xff0c;那么链表整体就可以代表一个整数。 给定两个这种链表&#xff0c;请生成代表两个整数相加值的结果链表。 数据范围&#xff1a;0 ≤ n,m ≤ 1000000&#xff0c;链表任意值 0 ≤ val ≤ 9 要求&#x…

Python绘制爱心代码(七夕限定版)

写在前面&#xff1a; 又到了一年一度的七夕节啦&#xff01;你还在发愁送女朋友什么礼物&#xff0c;不知道怎样表达你满满的爱意吗&#xff1f;别担心&#xff0c;我来帮你&#xff01;今天&#xff0c;我将教你使用Python绘制一个跳动的爱心&#xff0c;用创意和幽默为这个…

FL Studio21.1中文完整版Win/Mac

FL Studio All Plugins Edition【中文完整版 Win/Mac】适合音乐制作人/工作室使用&#xff0c;全套插件!&#xff08;20.9新增Vintage Chorus&#xff0c;Pitch Shifter变调插件&#xff09;FL Studio是超多顶级音乐人的启蒙首选&#xff01;包括百大DJ冠军Martin Garrix&…

《vue3实战》运用splice方法实现电影评价系统的查看、修改、删除功能

目录 前言 电影评价系统是什么&#xff1f;它能具有什么功能的体现&#xff1f; 一、splice方法的含义和作用 splice是什么&#xff1f;splice的作用体现在哪些方面&#xff1f; 二、功能实现 以下是实现查看逻辑功能的代码 以下是实现修改逻辑功能的代码 以下是实现删…