【基础篇】十三、强软弱虚引用、终结器引用

news2024/11/19 19:37:50

文章目录

  • 0、相关🖊
  • 1、强引用
  • 2、软引用
  • 3、弱引用
  • 4、虚引用
  • 5、终结引用

关于对象能否被回收:

  • 计数器
  • 可达性分析

还可以根据引用的类型,不同的引用类型,对应对象的不同GC回收规则。

0、相关🖊

📕【强软弱虚】

在这里插入图片描述

1、强引用

  • 默认强引用,即把一个对象赋值给一个变量(也叫引用)
Object o = new Object();
  • GC时,有强引用的对象不会被回收,即使OOM了

Demo:

public class Demo {

    public static void main(String[] args) {

        Demo demo = new Demo();
        System.out.println("GC前: " + demo);
        System.gc();
        System.out.println("GC后: " + demo);
        //断掉强引用
        demo = null;
        System.gc();
        System.out.println("断掉强引用并GC: " + demo);
    }
}

在这里插入图片描述

2、软引用

  • 内存足够时,不会被GC回收
  • 内存不足时,才被GC回收
  • 包装为软引用:new SoftReference<对象类型>(对象)
    在这里插入图片描述
public class SoftReferenceDemo {
    public static void main(String[] args) {
        byte[] byte1 = new byte[1024 * 1024 * 100];
        SoftReference<byte[]> softReference = new SoftReference<>(byte1);
        byte1 = null;
        System.gc();
        System.out.println("内存充足时:" + softReference.get());
        try {
            byte[] bytes = new byte[1024 * 1024 * 100];
        } catch (Error e) {
            e.printStackTrace();
        } finally {
            System.out.println("内存不足时:" + softReference.get());
        }

    }

}
public class SoftReferenceDemo {
    public static void main(String[] args) {
        SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024 * 100]);
        System.gc();
        System.out.println("内存充足时:" + softReference.get());
        try {
            byte[] bytes = new byte[1024 * 1024 * 100];
        } catch (Error e) {
            e.printStackTrace();
        } finally {
            System.out.println("内存不足时:" + softReference.get());
        }

    }

}

注意上面两份代码的区别,前者必须加个byte1 = null,这是个强引用,不断掉,即使内存不够,byte1对象也不会被回收,soft Reference.get结果也就一直不为null。这个地方卡了半小时,想着怎么还不回收,看半天发现这儿有个强引用。设置-Xmx200m,运行:

在这里插入图片描述

以上代码,盒子里的东西已经没了(被包装的对象被回收,get得到结果为null了),盒子也就没必要再留了。 但盒子里的东西何时被回收不确定,不能直接写一句先把盒子干掉:

softReference = null;

软引用中的对象如果在内存不足时回收,SoftReference对象本身也需要被回收:
在这里插入图片描述

  • 创建软引用时,构造方法里再传入一个引用队列
  • 对象A被回收,外层的SoftReference对象会加入队列
  • 遍历干掉外层的SoftReference

Demo:

public class SoftReferenceDemo {
    public static void main(String[] args) {
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
        ArrayList<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            byte[] byte1 = new byte[1024 * 1024 * 100];
            //包装对象时再传个队列
            SoftReference<byte[]> softReference = new SoftReference<>(byte1, queue);
            list.add(softReference);
        }

        int count = 0;
        //能从队里拿出来的,都是对象被回收的
        while (queue.poll() != null) {
            ++count;
        }
        System.out.println(count);
    }

}

设置JVM堆内存total和max为200M(实际可用约190M左右),循环十次,自然有九个byte[ ] 对象回收,queue长度应该是9:

在这里插入图片描述

poll弹出的就是被回收掉内存对象的SoftReference对象。

3、弱引用

  • 不管JVM内存是否够用,GC运行,弱引用对象均被回收。
  • 和软引用一样,也可搭配一个引用队列
  • 用于ThreadLocal应对内存泄漏

在这里插入图片描述

public class Demo {

    public static void main(String[] args) {

        WeakReference<Demo> reference = new WeakReference<>(new Demo());
        System.out.println("GC前: " + reference.get());
        System.gc();
        System.out.println("GC后: " + reference.get());
        
    }
}

在这里插入图片描述

4、虚引用

  • 幽灵引用/幻影引用
  • 虚,形同虚设的意思
  • 和其他几种引用不一样,它不影响对象的回收规则
  • 仅有虚引用指向的对象,随时可能会被回收
  • 唯一的用途是当对象被垃圾回收器回收时可以接收到对应的通知
  • 虚引用get方法返回结果总为null

虚引用的一个应用场景是直接内存的释放问题:

public class Demo {

    public static final int size = 1024 * 1024 * 10;

    public static void main(String[] args) {
        /**
         * allocateDirect方法创建DirectByteBuffer对象
         * DirectByteBuffer对象构造方法里向操作系统申请了直接内存
         */
        ByteBuffer directBuffer = ByteBuffer.allocateDirect(size);
        //干掉强引用
        directBuffer = null;
        System.gc();
        System.out.println();

    }
}

DirectByteBuffer对象被回收的时候,需要收到一个消息,去把直接内存的空间也释放了(不能只GC把堆里的DirectByteBuffer对象空间释放了,GC主要是处理堆,不是处理直接内存的)

在这里插入图片描述

往下跟:

在这里插入图片描述

Cleaner类继承了虚引用类,这里传入要监控的ByteBuffer对象,告诉虚引用我要监控这个对象的回收,接下来会有一个线程去监控这个对象的回收,

在这里插入图片描述

当ByteBuffer对象被回收,就调用Deallocator类(实现了Runnable接口)的run方法,run方法里干的活儿就是释放了直接内存:

在这里插入图片描述

贴个清晰点的Demo:

public class ReferenceDemo {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();
        PhantomReference<MyObject> phantomReference = new PhantomReference<>(myObject, referenceQueue);
        List<byte[]> list = new ArrayList<>();
        new Thread(() -> {
            while (true){
                list.add(new byte[1024 * 1024]); //1M
                //歇500ms,写1M进List
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //验证下每次get都是null
                System.out.println(phantomReference.get() + " list add OK.");
            }
        },"t1").start();

        new Thread(() -> {
            while (true){
                Reference<? extends MyObject> reference = referenceQueue.poll();
                if(reference != null){
                    System.out.println("有虚引用对象被回收,加入了队列");
                    //break;
                }
            }
        },"t2").start();
    }


}

5、终结引用

  • 对象需要被回收时,终结器引用会关联这个对象,并放入Finalizer类的引用队列
  • (无需手动编码,其内部配合引用队列使用)
  • 稍后会由FinalizerThread线程从队列中获取这个对象,并执行它的finalize方法
  • 在这个对象该第二次被回收时,才真正干掉这个对象

总结就是第一次包装一下扔到引用队列+执行finalize方法,第二次GC它时,抬走。根据这个特点,如果在第三步里的finalize方法里给变为null的对象,重新赋一个强引用,岂不是可以让这个对象复活。 Demo:

public class FinalizeReferenceDemo {

    public static FinalizeReferenceDemo reference = null;


    /**
     * 存活性验证
     */
    public void alive() {
        System.out.println("当前对象还存活...");
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            System.out.println("finalize方法执行===");
            //设置强引用自救
            reference = this;
        } finally {
            super.finalize();
        }

    }

    @SneakyThrows
    public static void test() {
        reference = null;
        System.gc();
        //执行finalize方法的优先级低,这里等一会儿再往下走
        Thread.sleep(500);
        if (reference != null) {  //若上面finalize方法执行,则这里不会为null了
            reference.alive();
        } else System.out.println("对象已被回收!");
    }

    public static void main(String[] args) {
        reference = new FinalizeReferenceDemo();
        test();
        test();
    }
}

运行结果:

在这里插入图片描述

test方法第一次调用,对象引用被置为null,并手动GC,该被回收了,此时进入引用队列并在稍后执行finalize方法。重写的finalize方法里给引用重新赋值,不为null了,test方法调alive方法发现对象又活了。

接着再第二次调test方法,按理说和第一次调test方法是一个流程,但finalize方法源码有说明:

在这里插入图片描述

即finalize方法最多被同一个JVM调用调用一次,对于一个被放弃的对象。所以第二次调test把引用又置为null并GC后,不会再调finalize方法,因此休眠500ms后,引用依然为null,对象被回收。

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

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

相关文章

【HarmonyOS开发】ArkUI-X 跨平台框架(使用ArkTs开发AndroidIOS)

ArkUI-X 跨平台框架进一步将 ArkUI 开发框架扩展到了多个OS平台&#xff0c;目前支持OpenHarmony、HarmonyOS、Android、 iOS&#xff0c;后续会逐步增加更多平台支持。开发者基于一套主代码&#xff0c;就可以构建支持多平台的精美、高性能应用。 一、跨平台框架有哪些? 1、…

java发送邮件到qq邮箱

自己的授权码自己记好 引入依赖 <dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><version>1.6.2</version> </dependency> <dependency><groupId>javax.mail</groupId>&…

[C#]使用OpenCvSharp实现区域文字提取

【官方框架地址】 github.com/shimat/opencvsharp 【算法介绍】 采用opencv算法实现文字区域提取&#xff0c;步骤如下&#xff1a; &#xff08;1&#xff09;形态学操作 &#xff08;2&#xff09;查找轮廓 &#xff08;3&#xff09;筛选那些面积小的 &#xff08;4&#…

【AI】人类视觉感知特性与深度学习模型(1/2)

目录 一、关于人类视觉感知 1.1 视觉关注 1.自上而下&#xff08;Top-down&#xff09;的视觉关注 ​编辑 2.自下而上&#xff08;Bottom-up&#xff09;的视觉关注 3.区别和记忆点 1.2 视觉掩盖 1.常见的视觉掩盖效应 2.恰可识别失真&#xff08;Just Noticeable Dif…

缓存数据库一致性问题

为什么使用缓存&#xff1f; 业务处于起步阶段&#xff0c;流量非常小&#xff0c;那无论是读请求还是写请求&#xff0c;直接操作数据库随着业务指数级增长&#xff0c;请求量剧增&#xff0c;直接访问数据库&#xff0c;导致性能急剧下降&#xff0c;需要引入缓存提高读性能…

win环境安装yarn脚手架

win环境安装yarn脚手架 1、安装命令 npm install -g yarn 2、查看安装的版本 yarn --version 报错了&#xff01;&#xff01;&#xff01; 解决方案 搜索​​ PowerShell​​&#xff0c;右键以管理员身份运行&#xff1b; 输入&#xff1a;​​set-ExecutionPolicy Remo…

知虾shopee数据:为卖家提供了丰富的数据分析工具

使用Shopee的卖家都知道&#xff0c;这个平台为卖家提供了丰富的数据分析工具&#xff0c;帮助他们更好地理解店铺运营状况和市场趋势。这些数据分析工具不仅能够提供数据总览&#xff0c;还包括买家分析、商品排名、分类排名、销售辅导、流量分析、销售结构、行销活动、聊天响…

性能优化-OpenMP概述(一)-宏观全面理解OpenMP

本文旨在从宏观角度来介绍OpenMP的原理、编程模型、以及在各个领域的应用、使用、希望读者能够从本文整体上了解OpenMP。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xff08;HPC&#xff09;开发基础…

整除判断-判断正整数a能否被b整除,如果不能整除,输出商和余数 C语言xdoj42

问题描述 判断正整数a能否被b整除&#xff0c;如果不能整除&#xff0c;输出商和余数 输入说明 输入两个正整数a和b&#xff08;0<a, b<10000&#xff09;&#xff0c;a和b之间用空格分隔。 输出说明 如果a能被b整除&#xff0c;输出yes&#xff0c;否则在同…

山区老人爱的礼物丨守护银龄,情暖寒冬

为让山区老人们在寒冷的冬天感受到来自社会的温暖&#xff0c;新年伊始&#xff0c;北京传益千里携手志愿者再次走进酉阳土家族苗族自治县木叶乡分发新的一轮山区老人爱的礼物&#xff0c;让更多的物资走向有需要的人群。 中午阳光正好&#xff0c;志愿者们走进山林中的人家&am…

文件销毁 硬盘销毁 数据销毁 物料销毁 淼一护航数据安全最后一公里

文件销毁、硬盘销毁、数据销毁以及物料销毁&#xff0c;是现代商业和行政管理中必须面对的重要环节。随着信息化程度的加深&#xff0c;数据安全和隐私保护已经成为全社会共同关注的焦点&#xff0c;而数据销毁则是确保信息安全的重要手段。淼一护航数据安全最后一公里&#xf…

开源协议简介和选择

软件国产化已经提到日程上了&#xff0c;先来研究一下开源协议。 引言 在追求“自由”的开源软件领域的同时不能忽视程序员的权益。为了激发程序员的创造力&#xff0c;现今世界上有超过60种的开源许可协议被开源促进组织&#xff08;Open Source Initiative&#xff09;所认可…

SCA面面观 | 企业该如何选择组件检测工具?

一般来说&#xff0c;一个软件应用程序可以被分解成若干部分&#xff0c;为软件程序解耦&#xff0c;以减少整个应用程序的复杂性&#xff0c;这些部分就是软件组件。以一种标准化的方式相互作用&#xff0c;使得组件可以像机器的“零部件”一样被换入或换出&#xff0c;因组件…

wait 和 notify 这个为什么要在synchronized 代码块中?

一个工作七年的小伙伴&#xff0c;竟然不知道” wait”和“notify”为什么要在 Synchronized 代码块中 。 好吧&#xff0c;如果屏幕前的你也不知道&#xff0c;请在公屏上刷”不知道“。 对于这个问题&#xff0c;我们来看看普通人和高手的回答。 一、问题解析 1. wait 和 n…

理解 Node.js 中的事件循环

你已经使用 Node.js 一段时间了&#xff0c;构建了一些应用程序&#xff0c;尝试了不同的模块&#xff0c;甚至对异步编程感到很舒适。但是有些事情一直在困扰着你——事件循环&#xff08;Event Loop&#xff09;。 如果你像我一样&#xff0c;花费了无数个小时阅读文档和观看…

将音频与视频格式互转的7 个顶级工具方法

你是否遇到过需要将视频文件从一种格式转换为另一种格式的情况&#xff1f; 在编辑家庭电影或者专业电影工作室工作&#xff0c;我们经常需要将视频文件转换成不同的格式。市场上有很多自称能够高效转换的工具&#xff0c;但是我们要时刻警惕诈骗工具&#xff0c;它们可能会耗…

【mac-m1 docker 安装upload-labs靶场】

1.搜索upload-labs docker search upload-labs 2.下载upload-labs docker pull c0ny1/upload-labs 3.启动 docker run -it -d --name uploadlabs -p 80:80 c0ny1/upload-labs --platform linux/amd64 4.访问127.0.0.1:80 注意点&#xff1a;后续使用的时候会报错 需要手动创…

BAT036:TXT与DOC格式互转、DOC与DOCX格式互转

引言:编写批处理程序,可实现txt与doc文档格式互转、doc与docx文档格式互转。 一、新建Windows批处理文件 参考博客: BAT002:在右键菜单新建中添加【Windows批处理文件】_为右键菜单添加bat-CSDN博客 二、TXT与DOC格式互转 1.右键新建的批处理文件,点击【编辑】。 ​ …

虚幻UE 增强输入-触发器

上一篇增强输入基础&#xff1a;虚幻UE 增强输入-第三人称模板增强输入分析与扩展 主要对第三人称模板的增强输入进行分析、复刻和扩展 本篇将会对增强输入中的触发器中的各参数进行讲解 文章目录 前言触发器参数1、下移TriggerDown2、已按下TriggerPressed3、已松开TriggerRel…

新手深入浅出理解PyTorch归一化层全解析

目录 torch.nn子模块normal层详解 nn.BatchNorm1d BatchNorm1d 函数简介 函数工作原理 参数详解 使用技巧与注意事项 示例代码 nn.BatchNorm2d BatchNorm2d 函数简介 函数工作原理 参数详解 使用技巧与注意事项 示例代码 nn.BatchNorm3d BatchNorm3d 函数简介 参…