JVM之四种引用类型(五)

JVM 系列吊打面试官:说一下 Java 的四种引用类型

image-20231205205234538

四种引种类型
1.强引用

在 Java 中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到 JVM 也不会回收。因此强引用是造成 Java 内存泄漏的主要原因之一。

2.软引用

软引用需要用 SoftReference 类来实现,对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中。

3.弱引用

弱引用需要用 WeakReference 类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够,总会回收该对象占用的内存。

4.虚引用

虚引用需要 PhantomReference 类来实现,它不能单独使用,必须和引用队列联合使用。虚引用的主要作用是跟踪对象被垃圾回收的状态。

我将它们的区别概括为 3 个维度:

  • 维度 1 - 对象可达性状态的区别: 强引用指向的对象是强可达的,而其他引用指向的对象都是弱可达的。当一个对象存在到 GC Root 的引用链时,该对象被认为是强可达的。只有强可达的对象才会认为是存活的对象,才能保证在垃圾收集的过程中不会被回收;

  • 维度 2 - 垃圾回收策略的区别: 除了影响对象的可达性状态,不同的引用类型还会影响垃圾收集器回收对象的激进程度:

    • 强引用: 强引用指向的对象不会被垃圾收集器回收;
    • 软引用: 软引用是相对于强引用更激进的策略,软引用指向的对象在内存充足时会从垃圾收集器中豁免,起到类似强引用的效果,但在内存不足时还是会被垃圾收集器回收。那么软引用通常是用于实现内存敏感的缓存,当有足够空闲内存时保留内存,当空闲内存不足时清理缓存,避免缓存耗尽内存;
    • 弱引用和虚引用: 弱引用和虚引用是相对于软引用更激进的策略,弱引用指向的对象无论在内存是否充足的时候,都会被垃圾收集器回收;
  • 维度 3 - 感知垃圾回收时机: 虚引用主要的作用是提供了一个感知对象被垃圾回收的机制。在虚拟机即将回收对象之前,如果发现对象还存在虚引用,则会在回收对象后会将引用加入到关联的引用队列中。程序可以通过观察引用队列的方式,来感知到对象即将被垃圾回收的时机,再采取必要的措施。例如 Java Cleaner 工具类,就是基于虚引用实现的回收工具类。需要特别说明的是,并不是只有虚引用才能与引用队列关联,软引用和弱引用都可以与引用队列关联,只是说虚引用唯一的作用就是感知对象垃圾回收时机。

除了我们熟悉的四大引用,虚拟机内部还设计了一个 @hideFinalizerReference 引用,用于支持 Java Finalizer 机制,更多内容见 Finalizer 机制。

指针、引用和句柄有什么区别?

引用、指针和句柄都具有指向对象地址的含义,可以将它们都简单地理解为一个内存地址。只有在具体的问题中,才需要区分它们的含义:

  • 1、引用(Reference): 引用是 Java 虚拟机为了实现灵活的对象生命周期管理而实现的对象包装类,引用本身并不持有对象数据,而是通过直接指针或句柄 2 种方式来访问真正的对象数据;
  • 2、指针(Point): 指针也叫直接指针,它表示对象数据在内存中的地址,通过指针就可以直接访问对象数据;
  • 3、句柄(Handler): 句柄是一种特殊的指针,句柄持有指向对象实例数据和类型数据的指针。使用句柄的优点是让对象在垃圾收集的过程中移动存储区域的话,虚拟机只需要改变句柄中的指针,而引用持有的句柄是稳定的。缺点是需要两次指针访问才能访问到对象数据。

直接指针访问:

image-20231205205712385

句柄访问:

image-20231205205745494

引用使用方法

这一节我们来讨论如何将引用与引用队列的使用方法。

使用引用对象
  • 1、创建引用对象: 直接通过构造器创建引用对象,并且直接在构造器中传递关联的实际对象和引用队列。引用队列可以为空,但虚引用必须关联引用队列,否则没有意义;
  • 2、获取实际对象: 在实际对象被垃圾收集器回收之前,通过 Reference#get() 可以获取实际对象,在实际对象被回收之后 get() 将返回 null,而虚引用调用 get() 方法永远是返回 null;
  • 3、解除关联关系: 调用 Reference#clear() 可以提前解除关联关系。

get() 和 clear() 最终是调用 native 方法,我们在后文分析。

SoftReference.java
// 已简化
public class SoftReference<T> extends Reference<T> {

    public SoftReference(T referent) {
        super(referent);
    }

    public SoftReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}
WeakReference.java
public class WeakReference<T> extends Reference<T> {

    public WeakReference(T referent) {
        super(referent);
    }

    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}
PhantomReference.java
public class PhantomReference<T> extends Reference<T> {

    // 虚引用 get() 永远返回 null
    public T get() {
        return null;
    }

    // 虚引用必须管理引用队列,否则没有意义
    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}
Reference.java
// 引用对象公共父类
public abstract class Reference<T> {

    // 虚拟机内部使用
    volatile T referent;

    // 关联引用队列
    final ReferenceQueue<? super T> queue;
 
    Reference(T referent) {
        this(referent, null);
    }

    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = queue;
    }

    // 获取引用指向的实际对象
    public T get() {
        // 调用 Native 方法
        return getReferent();
    }

    @FastNative
    private final native T getReferent();

    // 解除引用与实际对象的关联关系
    public void clear() {
        // 调用 Native 方法
        clearReferent();
    }

    @FastNative
    native void clearReferent();
    ...
}
引用队列使用模板

以下为 ReferenceQueue 的使用模板,主要分为 2 个阶段:

  • 阶段 1: 创建引用队列实例,并在创建引用对象时关联该队列;
  • 阶段 2: 对象在被垃圾回收后,引用对象会被加入引用队列(根据下文源码分析,引用对象在进入引用队列的时候,实际对象已经被回收了)。通过观察 ReferenceQueue#poll() 的返回值可以感知对象垃圾回收的时机。
示例程序
// 阶段 1:
// 创建对象
String strongRef = new String("abc");
// 1、创建引用队列
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
// 2、创建引用对象,并关联引用队列
WeakReference<String> weakRef = new WeakReference<>(strongRef, referenceQueue);
System.out.println("weakRef 1:" + weakRef);
// 3、断开强引用
strongRef = null;

System.gc();

// 阶段 2:
// 延时 5000 是为了提高 "abc" 被回收的概率
view.postDelayed(new Runnable() {
    @Override
    public void run() {
        System.out.println(weakRef.get()); // 输出 null
        // 观察引用队列
        Reference<? extends String> ref = referenceQueue.poll();
        if (null != ref) {
            System.out.println("weakRef 2:" + ref);
            // 虽然可以获取到 Reference 对象,但无法获取到引用原本指向的对象
            System.out.println(ref.get()); // 输出 null
        }
    }
}, 5000);
程序输出
I/System.out: weakRef 1:java.lang.ref.WeakReference@3286da7
I/System.out: null
I/System.out: weakRef 2:java.lang.ref.WeakReference@3286da7
I/System.out: null

ReferenceQueue 中大部分 API 是面向 Java 虚拟机内部的,只有 ReferenceQueue#poll() 是面向开发者的。它是非阻塞 API,在队列有数据时返回队头的数据,而在队列为空时直接返回 null。

ReferenceQueue.java
public Reference<? extends T> poll() {
    synchronized (lock) {
        if (head == null)
            return null;

        return reallyPollLocked();
    }
}
工具类 Cleaner 使用模板

Cleaner 是虚引用的工具类,用于实现在对象被垃圾回收时额外执行一段清理逻辑,本质上只是将虚引用和引用队列等代码做了简单封装而已。以下为 Cleaner 的使用模板:

示例程序
// 1、创建对象
String strongRef = new String("abc");
// 2、创建清理逻辑
CleanerThunk thunk = new CleanerThunk();
// 3、创建 Cleaner 对象(本质上是一个虚引用)
Cleaner cleaner = Cleaner.create(strongRef, thunk);

private class CleanerThunk implements Runnable {
    @Override
    public void run() {
        // 清理逻辑
    }
}
Cleaner.java
// Cleaner 只不过是虚引用的工具类而已
public class Cleaner extends PhantomReference<Object> {
    ...
}

jvm相关知识点持续更新中!喜欢的话请点赞、收藏、关注哦!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RPG项目01_UI面板Game

基于“RPG项目01_技能释放”&#xff0c;将UI包导入Unity场景中&#xff0c; 将图片放置 拖拽 取消勾选&#xff08;隐藏攻击切片&#xff09; 对技能添加蒙版 调节父子物体大小一致 将子类蒙版复制 执行5次 运行即可看到技能使用完的冷却条 在Scripts下创建UI文件夹 写代码&am…

设计模式——七大设计原则

设计模式——七大设计原则 1、单一职责原则&#xff08;SRP&#xff09;2、开放封闭原则&#xff08;OCP&#xff09;3、依赖倒转原则&#xff08;DIP&#xff09;4、里氏替换原则 (LSP)5、接口隔离原则 (ISP)6、合成/聚合复用原则 (CARP)7、迪米特法则 (LoD) 了解 设计模式 的…

使用C语言创建高性能网络爬虫IP池

目录 一、引言 二、IP池的设计 1、需求分析 2、架构设计 3、关键技术 三、IP池的实现 1、存储实现 2、调度实现 3、通信实现 4、异常处理实现 四、代码示例 五、性能优化 六、测试与分析 七、结论 一、引言 随着互联网的快速发展&#xff0c;网络爬虫成为了获取…

2-1、地址加法器CS:IP

语雀原文链接 文章目录 1、CPU组成2、通用寄存器16位寄存器的存储16位寄存器兼容8位word 和 byte进位问题 3、地址加法器不同的段地址和偏移地址表示同一个物理地址偏移地址的范围一个段的起始地址一定是16的倍数 4、CS:IPCS IP工作过程jmp修改CS:IP 5、DS和[address]DS和[add…

高级搜索——伸展树Splay详解

文章目录 伸展树Splay伸展树Splay的定义局部性原理Splay的伸展操作逐层伸展双层伸展zig-zig/zag-zagzig-zag/zag-zigzig/zag双层伸展的效果与效率 伸展树的实现动态版本实现递增分配器节点定义Splay类及其接口定义伸展操作左单旋右单旋右左/左右双旋伸展 查找操作删除操作插入操…

Spring 保姆级带你认识,让你如何轻松应对面试官

Spring 保姆级带你认识&#xff0c;让你如何轻松应对面试官 1.Spring是什么&#xff1f;作用是什么&#xff1f; Spring是一个轻量级的JavaEE框架&#xff0c;它主要解决企业应用中的复杂性问题。Spring框架有三个核心部分&#xff1a;IoC容器、AOP和数据访问/集成层。Spring…

java--抽象类

1.什么是抽象类 ①在java中有一个关键字叫&#xff1a;abstract&#xff0c;它就是抽象的意思&#xff0c;可以用它修饰类、成员方法。 ②abstract修饰类&#xff0c;这个类就是抽象类&#xff1b;修饰方法&#xff0c;这个方法就是抽象方法。 2.抽象类的注意事项、特点 ①抽…

Vue3-ElementPlus按需导入

1.安装 pnpm add element-plus 2.配置按需导入&#xff1a; 官方文档&#xff1a;快速开始 | Element Plus 按照官网按需导入中的自动导入步骤来进行 pnpm add -D unplugin-vue-components unplugin-auto-import 观察Vite代码与原vite文件的差别&#xff0c;将原vite文件中没…

[数据结构]-map和set

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、键值对…

【数据结构】二叉树的实现

目录 1. 前言2. 二叉树的实现2.1 创建一棵树2.2 前序遍历2.2.1 分析2.2.2 代码实现2.2.3 递归展开图 2.3 中序遍历2.3.1 分析2.3.2 代码实现2.3.3 递归展开图 2.4 后序遍历2.4.1 分析2.4.2 代码实现2.4.3 递归展开图 2.5 求节点个数2.5.1 分析2.5.2 代码实现 2.6 求叶子节点个数…

Linux 环境下的性能测试——top与stress

对于Linux 环境&#xff0c;top命令是使用频繁且信息较全的命令&#xff0c; 它对于所有正在运行的进行和系统负荷提供实时更新的概览信息。stress是个简单且全面的性能测试工具。通过它可以模拟各种高负载情况。 通过top与stress这两个命令的结合使用&#xff0c;基本可以达到…

解决:ModuleNotFoundError: No module named ‘exceptions’

解决&#xff1a;ModuleNotFoundError: No module named ‘exceptions’ 文章目录 解决&#xff1a;ModuleNotFoundError: No module named exceptions背景报错问题翻译&#xff1a;报错位置代码报错原因解决方法今天的分享就到此结束了 背景 在使用之前的代码时&#xff0c;报…

报表控件Stimulsoft 操作演示:访问编译的报告

使用编译计算模式的报告能够执行使用报告脚本语言实现的各种脚本。然而&#xff0c;这些场景并不总是安全的&#xff0c;从网络安全的角度来看&#xff0c;可能会导致负面情况。经过分析情况&#xff0c;我们决定加强有关编译模式报告的安全策略。但让我们一步一步来。顺便说一…

IDEA版SSM入门到实战(Maven+MyBatis+Spring+SpringMVC) -Mybatis核心配置详解

第一章 Mybatis核心配置详解【mybatis-config.xml】 1.1 核心配置文件概述 MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 1.2 核心配置文件根标签 没有实际语义&#xff0c;主要作用&#xff1a;所有子标签均需要设置在跟标签内部 1.3 核心配置文件…

【数电笔记】17-具体函数的卡诺图填入

目录 说明&#xff1a; 用卡诺图表示逻辑函数 1. 基本步骤 2. 例题 2.1 例1-真值表转换卡诺图 2.2 例2-标准与或式画卡诺图 2.3 例3-非标准与或式画卡诺图&#xff08;常见,重点掌握&#xff09; 说明&#xff1a; 笔记配套视频来源&#xff1a;B站&#xff1b;本系列笔…

学生档案管理系统研究

摘 要 学生档案管理系统是一个教育单位不可缺少的部分,它的内容对于学校的决策者和管理者来说都至关重要,所以学生档案管理系统应该能够为用户提供充足的信息和快捷的查询手段。但一直以来人们使用传统人工的方式管理文件档案&#xff0c;这种管理方式存在着许多缺点,如:效率低…

gpt阅读论文利器

1. txyz.ai 读论文 严伯钧 3. consensus 两亿科学论文的资源库. 用英文. 中国经济发展, 美国加州没有,减肥没有. 2. chrome插件 gpt sidebar 3. gpt academic 论文润色和学术翻译 ,一键输出公式. 英语口语8000句. 托福备考计划表. 百词斩托福. 薄荷外刊. 分区笔记精读法.…

java--接口的其他细节

1.jdk8开始&#xff0c;接口新增了三种形式的方法 ①默认方法(实例方法)&#xff1a;使用用default修饰&#xff0c;默认会被加上public修饰。注意&#xff1a;只能使用接口的实现类对象调用 ②私有方法&#xff1a;必须用private修饰(jdk9开始才支持) ③类方法(静态方法)&a…

数字化车间|用可视化技术提升车间工作效率

数字化车间正在成为现代制造业的重要组成部分。随着科技的不断进步&#xff0c;传统的车间生产方式逐渐地被数字化和自动化取代。数字化车间将机器和软件进行整合&#xff0c;实现了生产过程的高效、精确和可追溯。在数字化车间中&#xff0c;机器之间可以进行无缝的通信和协作…

vue: 线上项目element-ui的icon偶尔乱码问题

线上环境偶尔会复现&#xff0c; 具体&#xff1a; 一般使用不会出现这个问题&#xff0c;因为一般引入的是element-ui的css文件&#xff0c;问题出在于为了主题色变化啊&#xff0c;需要用到scss变量引入了scss文件。 import “~element-ui/packages/theme-chalk/src/index”…