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

文章目录

  • 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进Listtry {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}//验证下每次get都是nullSystem.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("当前对象还存活...");}@Overrideprotected void finalize() throws Throwable {try {System.out.println("finalize方法执行===");//设置强引用自救reference = this;} finally {super.finalize();}}@SneakyThrowspublic 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.mzph.cn/news/598278.shtml

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

相关文章

高质量发展项目——糖尿病管理医药共话交流会在京成功举行

为积极配合国家在卫生事业改革发展的前进方向&#xff0c;抓好医院管理的顶层设计&#xff0c;中国初级卫生保健基金会公共卫生专业委员会与国家卫生健康委人才交流服务中心于2023年7月18日成功举行高质量发展项目—糖尿病管理医药共话交流会。此次会议邀请全国内分泌领域及药学…

gRPC 为什么这么快?

gRPC 为什么这么快&#xff1f; 本文转自 公众号 ByteByteGo&#xff0c;如有侵权&#xff0c;请联系&#xff0c;立即删除 RPC&#xff08;Remote Procedural Call, 远程过程调用&#xff09;之所以被称为 remote&#xff0c;因为在微服务架构下&#xff0c;RPC 可以实现远程服…

【大数据】安装 Zookeeper 单机版

安装 Zookeeper 单机版 下面安装 Zookeeper&#xff0c;由于它是 Apache 的一个顶级项目&#xff0c;所以域名是 zookeeper.apache.org&#xff0c;所有 Apache 的顶级项目的官网都是以项目名 .apache.org 来命名的。 点击 Download 即可下载&#xff0c;这里我们选择的版本是 …

Python 简单爬虫程序及其工作原理

前言 网络中包含大量的数据&#xff0c;这些数据对于我们来说是非常有价值的&#xff0c;因此编写一个爬虫程序&#xff0c;自动从网页中获取所需的数据&#xff0c;对于信息收集和分析是非常有帮助的。Python 是一种高效而灵活的编程语言&#xff0c;它提供了强大的库和框架来…

软考报名有哪些要求?

报考任何级别不需要学历、资历条件&#xff0c;只要达到相应的专业技术水平就可以报考相应的级别 (一)2024年软考报名入口 2024年软考采用网络报名方式&#xff0c;考生在报名期间进入中国计算机技术职业资格网 (中国计算机技术职业资格网)&#xff0c;点击页面右下角的报名入…

JAVA学习专栏

JAVA专栏 Java核心技术 Java核心技术 Java练手算法 Java练手算法 Java数据结构和算法 Java数据结构和算法 Java设计模式 Java设计模式 Java并发编程 Java并发编程 MySQL数据库 MySQL数据库 Java项目管理Maven Java项目管理Maven 项目管理工具gradle 项目管理工具gradle…

分布式锁Lock4J 使用总结

Lok4j 简介 lock4j是一个分布式锁组件&#xff0c;其提供了多种不同的支持以满足不同性能和环境的需求。 立志打造一个简单但富有内涵的分布式锁组件。 特点 简单易用&#xff0c;功能强大&#xff0c;扩展性强。支持redission,redisTemplate,zookeeper。可混用&#xff0c…

分布式【Zookeeper三大核心之数据节点ZNode】

ZooKeeper在分布式领域&#xff0c;能够帮助解决很多很多的分布式难题&#xff0c;但是底层却只是依赖于两个主要的组件&#xff1a;ZNode文件/数据存储系统和watch监听系统&#xff0c;另外还有一大模块&#xff0c;就是ACL系统。本节我们介绍下znode文件/数据存储系统。 一、…

【QT】自定义代理类

目录 1 我们为什么要使用自定义代理类&#xff1f; 2 自定义代理类的基本设计要求 3 自定义代理的功能 4 基于QSpinBox的自定义代理类 5 自定义代理类的使用 1 我们为什么要使用自定义代理类&#xff1f; 传统的模型-视图框架可以让我们实现逻辑展示相分离&#xff0c;我们…

Spring见解 1.2 IOC

2.3.Spring的IOC解决程序耦合 2.3.1.创建工程 2.3.1.1.pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:sc…

Spring 七种事务传播性介绍

作者&#xff1a;vivo 互联网服务器团队 - Zhou Shaobin 本文主要介绍了Spring事务传播性的相关知识。 Spring中定义了7种事务传播性&#xff1a; PROPAGATION_REQUIRED PROPAGATION_SUPPORTS PROPAGATION_MANDATORY PROPAGATION_REQUIRES_NEW PROPAGATION_NOT_SUPPORTED…

八、QLayout 用户基本资料修改(Qt5 GUI系列)

目录 一、设计需求 二、实现代码 三、代码解析 四、总结 一、设计需求 在很多应用程序中会有用户注册或用户编辑信息等界面。本文就设计一个用户信息编辑界面。要求包含用户名、姓名、性别、部门、年龄、头像、个人说明等信息。 二、实现代码 #ifndef DIALOG_H #define D…

springboot中引入AOP切面编程

在Spring Boot 3.0中引入AOP的过程如下所示&#xff1a; 1、首先&#xff0c;确保已经添加了相关依赖。可以通过Maven或Gradle来管理项目的依赖。对于使用Maven构建的项目&#xff0c;需要将以下依赖添加到pom.xml文件中 <dependency><groupId>org.springframewo…

BERT(从理论到实践): Bidirectional Encoder Representations from Transformers【3】

这是本系列文章中的第3弹,请确保你已经读过并了解之前文章所讲的内容,因为对于已经解释过的概念或API,本文不会再赘述。 本文要利用BERT实现一个“垃圾邮件分类”的任务,这也是NLP中一个很常见的任务:Text Classification。我们的实验环境仍然是Python3+Tensorflow/Keras…

2023年度全球重大关基安全事件 TOP 10 | FreeBuf 年度盘点

2023年&#xff0c;针对关键信息基础设施的网络攻击已经演变成为了一个全球性的问题&#xff0c;无论是中、美、俄等国际大国&#xff0c;还是诸多小国/地区&#xff0c;无论是经济发达还是落后&#xff0c;都无法保证绝对免疫关键基础设施的攻击。为了保障国家安全和社会稳定&…

力扣-42.接雨水

题目&#xff1a; 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组[0,1,0,2…

Windows PowerShell的安全目标——安全警报

Windows PowerShell的安全目标——安全警报 1. 保证Shell安全 ​ 自从2006年年底PowerShell发布以来&#xff0c;微软在安全和脚本方面并没有取得很好的名声。毕竟那个时候&#xff0c;**VBScript和Windows Script Host(WSH)**是两个最流行的病毒和恶意软件的载体&#xff0c…

Linux_CentOS_7.9_MySQL_5.7配置数据库服务开机自启动之简易记录

前言&#xff1a; 作为运维保障&#xff0c;都无法准确预估硬件宕机的突发阶段&#xff0c;其生产数据实时在产出&#xff0c;那作为dba数据库服务的其重要性、必要性就突显而出。这里拿虚拟机试验做个配置记录&#xff0c;便于大家学习参考。 # 如出现服务器重启后登入报错无…

彻底理解前端安全面试题(4)—— 中间人攻击,详解 http 和https 的中间人攻击实例,建议收藏(含源码)

前言 前端关于网络安全问题看似高深莫测&#xff0c;其实来来回回就那么点东西&#xff0c;我总结一下就是 3 1 4&#xff0c;3个用字母描述的【分别是 XSS、CSRF、CORS】 一个中间人攻击。当然 CORS 同源策略是为了防止攻击的安全策略&#xff0c;其他的都是网络攻击。除…

docker-compose Install spug 3

前言 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台,整合了主机管理、主机批量执行、主机在线终端、文件在线上传下载、应用发布部署、在线任务计划、配置中心、监控、报警等一系列功能。 创建一键安装spug 脚本 自动化脚本兼容(ubuntu,RedHat系列及复刻系列,…