Java - 引用类型:强引用、软引用、弱引用和虚引用详解

文章目录

  • 概述
    • 1. 强引用(Strong Reference)
      • 1.1 什么是强引用?
      • 1.2 强引用的特点
      • 1.3 强引用的使用场景
      • 1.4 强引用的注意事项
    • 2. 软引用(Soft Reference)
      • 2.1 什么是软引用?
      • 2.2 软引用的特点
      • 2.3 软引用的使用场景
      • 2.4 软引用的注意事项
    • 3. 弱引用(Weak Reference)
      • 3.1 什么是弱引用?
      • 3.2 弱引用的特点
      • 3.3 弱引用的使用场景
      • 3.4 弱引用的注意事项
    • 4. 虚引用(Phantom Reference)
      • 4.1 什么是虚引用?
      • 4.2 虚引用的特点
      • 4.3 虚引用的使用场景
      • 4.4 虚引用的注意事项
    • 5. 引用队列(ReferenceQueue)
      • 5.1 什么是引用队列?
      • 5.2 引用队列的使用场景
      • 5.3 引用队列的注意事项
  • 使用案例:对象回收跟踪与资源清理
    • 实现步骤
    • 代码实现
      • 1. 资源类
      • 2. 资源清理类
      • 3. 资源管理类
      • 4. 测试代码
    • 代码运行结果
      • 结果分析
    • 关键点解析
    • 扩展:定期检查引用队列
      • 使用守护线程定期检查
      • 测试代码
      • 运行结果
    • 总结

在这里插入图片描述

概述

在Java中,内存管理是一个非常重要的主题。Java的垃圾回收机制(Garbage Collection, GC)自动管理内存,但开发者仍然需要了解如何通过引用类型来控制对象的生命周期。Java提供了四种引用类型:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。每种引用类型对垃圾回收的影响不同,适用于不同的场景。

接下来我们将深入探讨这四种引用类型,并结合实际代码示例以便能够更好地理解它们的使用场景和工作原理。同时,还会介绍引用队列(ReferenceQueue)的作用,以及如何利用它来跟踪对象的回收状态。


1. 强引用(Strong Reference)

1.1 什么是强引用?

强引用是Java中最常见的引用类型。如果一个对象具有强引用,垃圾回收器不会回收该对象,即使内存不足时也不会回收。强引用是默认的引用类型,我们在日常开发中使用的绝大多数引用都是强引用。

Object obj = new Object(); // obj 是一个强引用

1.2 强引用的特点

  • 对象不会被回收:只要强引用存在,对象就不会被垃圾回收器回收。
  • 显式释放:只有当强引用被显式地设置为null,或者超出作用域时,对象才会被垃圾回收。
obj = null; // 现在对象可以被回收

1.3 强引用的使用场景

强引用适用于那些必须长期存在的对象。例如,核心业务逻辑中的对象、单例对象等。由于强引用会阻止垃圾回收,因此在使用强引用时需要注意避免内存泄漏。

1.4 强引用的注意事项

  • 内存泄漏:如果强引用一直存在,但对象已经不再使用,可能会导致内存泄漏。例如,缓存中的对象如果没有及时清理,可能会导致内存占用过高。
  • 显式释放:在不再需要对象时,应该显式地将强引用设置为null,以帮助垃圾回收器及时回收内存。

2. 软引用(Soft Reference)

2.1 什么是软引用?

软引用用于描述一些还有用但并非必需的对象。只有在内存不足时,垃圾回收器才会回收软引用指向的对象。软引用比强引用弱,但比弱引用强。

SoftReference<Object> softRef = new SoftReference<>(new Object());

2.2 软引用的特点

  • 内存不足时回收:当内存充足时,软引用指向的对象不会被回收;但当内存不足时,垃圾回收器会回收这些对象。
  • 适合缓存:软引用通常用于实现内存敏感的缓存。例如,缓存图片、文件等资源时,可以使用软引用。
Object obj = softRef.get(); // 获取软引用指向的对象,可能为null

2.3 软引用的使用场景

软引用非常适合用于实现缓存。例如,在Android开发中,可以使用软引用来缓存图片资源。当内存充足时,图片资源会保留在缓存中;当内存不足时,垃圾回收器会自动回收这些资源,避免内存溢出。

2.4 软引用的注意事项

  • 性能开销:软引用的实现需要额外的开销,因此在性能敏感的场景中需要谨慎使用。
  • 不可靠性:由于软引用指向的对象可能会被回收,因此在获取对象时需要进行空值检查。

3. 弱引用(Weak Reference)

3.1 什么是弱引用?

弱引用比软引用更弱一些。弱引用指向的对象在下一次垃圾回收时会被回收,无论内存是否充足。

WeakReference<Object> weakRef = new WeakReference<>(new Object());

3.2 弱引用的特点

  • 立即回收:弱引用指向的对象在下一次垃圾回收时会被回收,即使内存充足。
  • 适合临时缓存:弱引用通常用于实现临时缓存或映射表,允许对象在没有强引用时被回收。
Object obj = weakRef.get(); // 获取弱引用指向的对象,可能为null

3.3 弱引用的使用场景

弱引用非常适合用于实现临时缓存。例如,在Java的WeakHashMap中,键对象是通过弱引用保存的。当键对象没有其他强引用时,垃圾回收器会自动回收它,并从WeakHashMap中移除对应的条目。

3.4 弱引用的注意事项

  • 对象生命周期短:由于弱引用指向的对象会被立即回收,因此不适合用于需要长期保存的对象。
  • 空值检查:在获取弱引用指向的对象时,必须进行空值检查。

4. 虚引用(Phantom Reference)

4.1 什么是虚引用?

虚引用是最弱的一种引用类型。虚引用无法通过get()方法获取到对象,它的存在只是为了在对象被回收时收到一个系统通知。

ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);

4.2 虚引用的特点

  • 无法获取对象:虚引用的get()方法总是返回null
  • 回收通知:虚引用主要用于跟踪对象被垃圾回收的状态。当对象被回收时,虚引用会被放入关联的ReferenceQueue中。
Object obj = phantomRef.get(); // 总是返回null

4.3 虚引用的使用场景

虚引用通常用于实现资源清理机制。例如,在Java的DirectByteBuffer中,虚引用用于在对象被回收时释放直接内存。

4.4 虚引用的注意事项

  • 无法获取对象:由于虚引用的get()方法总是返回null,因此无法通过虚引用直接访问对象。
  • 复杂的实现:虚引用的实现通常比较复杂,需要结合ReferenceQueue使用。

5. 引用队列(ReferenceQueue)

5.1 什么是引用队列?

引用队列可以与软引用、弱引用和虚引用一起使用。当引用指向的对象被回收时,引用本身会被放入引用队列中。开发者可以通过检查队列来得知对象已被回收。

ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakReference<Object> weakRef = new WeakReference<>(new Object(), queue);

// 当对象被回收时,weakRef会被放入queue中

5.2 引用队列的使用场景

引用队列通常用于实现对象回收的跟踪机制。例如,在实现自定义缓存时,可以使用引用队列来清理被回收的对象。

5.3 引用队列的注意事项

  • 队列检查:需要定期检查引用队列,以处理被回收的对象

引用队列(ReferenceQueue)通常与软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference)一起使用。当引用指向的对象被垃圾回收器回收时,引用本身会被放入引用队列中。通过定期检查引用队列,开发者可以得知哪些对象已经被回收,从而执行一些清理操作。


使用案例:对象回收跟踪与资源清理

假设我们有一个资源管理类,负责管理一些需要清理的资源(例如文件句柄、网络连接等)。我们希望在这些资源被垃圾回收时,自动执行清理操作。为了实现这一点,我们可以使用虚引用(PhantomReference)和引用队列(ReferenceQueue)。

实现步骤

  1. 创建一个资源类,表示需要管理的资源。
  2. 使用虚引用和引用队列跟踪资源的回收状态。
  3. 定期检查引用队列,执行资源清理操作。

代码实现

1. 资源类

首先,我们定义一个资源类 Resource,表示需要管理的资源。

class Resource {
    private String name;

    public Resource(String name) {
        this.name = name;
        System.out.println("Resource created: " + name);
    }

    public void close() {
        System.out.println("Resource closed: " + name);
    }
}

2. 资源清理类

接下来,我们定义一个资源清理类 ResourceCleaner,用于跟踪资源的回收状态并执行清理操作。

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class ResourceCleaner extends PhantomReference<Resource> {
    private String name;

    public ResourceCleaner(Resource resource, ReferenceQueue<? super Resource> queue) {
        super(resource, queue);
        this.name = resource.toString(); // 保存资源的标识
    }

    public void clean() {
        // 执行资源清理操作
        System.out.println("Cleaning up resource: " + name);
    }
}

3. 资源管理类

然后,我们定义一个资源管理类 ResourceManager,负责管理资源并定期检查引用队列。

import java.lang.ref.ReferenceQueue;

public class ResourceManager {
    private ReferenceQueue<Resource> queue = new ReferenceQueue<>();

    public void registerResource(Resource resource) {
        // 创建虚引用并关联引用队列
        ResourceCleaner cleaner = new ResourceCleaner(resource, queue);
        System.out.println("Resource registered: " + resource);
    }

    public void checkQueue() {
        // 检查引用队列,处理被回收的资源
        ResourceCleaner cleaner = (ResourceCleaner) queue.poll();
        while (cleaner != null) {
            cleaner.clean(); // 执行清理操作
            cleaner = (ResourceCleaner) queue.poll();
        }
    }
}

4. 测试代码

最后,我们编写测试代码来验证资源回收和清理机制。

public class ReferenceQueueExample {
    public static void main(String[] args) throws InterruptedException {
        ResourceManager manager = new ResourceManager();

        // 创建资源并注册
        Resource resource1 = new Resource("Resource-1");
        manager.registerResource(resource1);

        // 模拟资源不再被强引用
        resource1 = null;

        // 触发垃圾回收
        System.gc();

        // 等待一段时间,确保垃圾回收完成
        Thread.sleep(1000);

        // 检查引用队列并执行清理操作
        manager.checkQueue();
    }
}

代码运行结果

运行上述代码后,输出如下:

Resource created: Resource-1
Resource registered: Resource@1b6d3586
Cleaning up resource: Resource@1b6d3586

结果分析

  1. 创建了一个资源对象 Resource-1,并将其注册到 ResourceManager 中。
  2. resource1 设置为 null,使其不再被强引用。
  3. 调用 System.gc() 触发垃圾回收。
  4. 垃圾回收器回收了 Resource-1,并将其虚引用放入引用队列。
  5. 调用 manager.checkQueue() 检查引用队列,并执行资源清理操作。

关键点解析

  1. 虚引用的作用

    • 虚引用无法通过 get() 方法获取对象,因此不会影响对象的生命周期。
    • 虚引用的主要作用是跟踪对象被回收的状态。
  2. 引用队列的作用

    • 当虚引用指向的对象被回收时,虚引用会被放入引用队列。
    • 通过检查引用队列,可以得知哪些对象已经被回收。
  3. 资源清理机制

    • 在资源被回收后,通过引用队列执行清理操作(例如关闭文件句柄、释放内存等)。
    • 这种机制可以避免资源泄漏。

扩展:定期检查引用队列

在实际应用中,可能需要定期检查引用队列,以确保及时清理被回收的资源。可以通过以下方式实现定期检查:

使用守护线程定期检查

public class ResourceManager {
    private ReferenceQueue<Resource> queue = new ReferenceQueue<>();
    private Thread cleanupThread;

    public ResourceManager() {
        // 启动一个守护线程定期检查引用队列
        cleanupThread = new Thread(() -> {
            while (true) {
                try {
                    ResourceCleaner cleaner = (ResourceCleaner) queue.remove();
                    cleaner.clean();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        cleanupThread.setDaemon(true);
        cleanupThread.start();
    }

    public void registerResource(Resource resource) {
        ResourceCleaner cleaner = new ResourceCleaner(resource, queue);
        System.out.println("Resource registered: " + resource);
    }
}

测试代码

public class ReferenceQueueExample {
    public static void main(String[] args) throws InterruptedException {
        ResourceManager manager = new ResourceManager();

        // 创建资源并注册
        Resource resource1 = new Resource("Resource-1");
        manager.registerResource(resource1);

        // 模拟资源不再被强引用
        resource1 = null;

        // 触发垃圾回收
        System.gc();

        // 等待一段时间,确保垃圾回收完成
        Thread.sleep(1000);
    }
}

运行结果

Resource created: Resource-1
Resource registered: Resource@1b6d3586
Cleaning up resource: Resource@1b6d3586

总结

通过引用队列,我们可以跟踪对象的回收状态,并在对象被回收后执行清理操作。这种机制非常适合用于资源管理、缓存清理等场景。在实际应用中,可以结合守护线程定期检查引用队列,确保及时清理被回收的资源。

在这里插入图片描述

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

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

相关文章

S4 HANA给科目分配允许记账的税码

本文主要介绍在S4 HANA OP中给科目分配允许记账的税码相关设置。具体请参照如下内容&#xff1a; 1. 给科目分配允许记账的税码 以上配置定义了总账科目可以使用什么税码进行记账。通常在科目主数据中会明确总账科目的“Tax Category”来请明确总账科目可以使用什么类型的税码…

xss-labs靶场

xss-labs靶场 xss攻击类型 反射型xss 即攻击者将恶意脚本嵌入到url或者表单中&#xff0c;当用户访问特定的url或者提交表单时&#xff08;用户端请求时)&#xff0c;恶意脚本会执行 攻击需要用户点击恶意链接或访问包含恶意参数的url触发 存储型xss 即攻击者将恶意脚本提交…

CVE-2024-23897-Jenkins任意文件读取漏洞复现

content Jenkins是什么CVE-2024-23897总结修复建议 Jenkins是什么 Jenkins是一人基于Java开发的、可扩展的持续集成引擎&#xff0c;用于持续、自动地构建/测试软件项目&#xff0c;可以监控一些定时执行的任务。 官网文档&#xff1a; Jenkins是一款开源 CI&CD 软件&…

解析 Oracle 中的 ALL_SYNONYMS 和 ALL_VIEWS 视图:查找同义词与视图的基础操作

目录 前言1. ALL_SYNONYMS 视图2. ALL_VIEWS 视图3. 扩展 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 1. ALL_SYNONYMS 视图 在 Oracle 数据库中&#xff0c;同义词&#xff08;Synonym&#xff09;是对数…

30.Word:设计并制作新年贺卡以及标签【30】

目录 NO1.2 NO3邮件合并-信函 NO4邮件合并-标签​ NO1.2 另存为/F12&#xff1a;考生文件夹&#xff1a;Word.docx布局→页面设置对话框→页边距&#xff1a;上下左右→纸张&#xff1a;宽度/高度&#xff08;先调页边距&#x1f197;&#xff09;设计→页面颜色→填充效果→…

Unity实现按键设置功能代码

一、前言 最近在学习unity2D&#xff0c;想做一个横版过关游戏&#xff0c;需要按键设置功能&#xff0c;让用户可以自定义方向键与攻击键等。 自己写了一个&#xff0c;总结如下。 二、界面效果图 这个是一个csv文件&#xff0c;准备第一列是中文按键说明&#xff0c;第二列…

一个简单的自适应html5导航模板

一个简单的 HTML 导航模板示例&#xff0c;它包含基本的导航栏结构&#xff0c;同时使用了 CSS 进行样式美化&#xff0c;让导航栏看起来更美观。另外&#xff0c;还添加了一些 JavaScript 代码&#xff0c;用于在移动端实现导航菜单的展开和收起功能。 PHP <!DOCTYPE htm…

TensorFlow 示例摄氏度到华氏度的转换(一)

TensorFlow 实现神经网络模型来进行摄氏度到华氏度的转换&#xff0c;可以将其作为一个回归问题来处理。我们可以通过神经网络来拟合这个简单的转换公式。 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与预测 7. 保存与加载模型 …

2.1.3 相机图像信号处理的基本流程

文章目录 ISP基本流程ISP各基本流程职责 ISP基本流程 图像信号处理将传感器采集到的Bayer阵列数据转换成符合人眼观感的图像数据。ISP(Image Signal Processing)图像信号处理基本流程包括坏点校正&#xff08;DPC, Defect Pixel Correction&#xff09;&#xff0c;黑电平校正&…

51单片机CLD1602显示万年历+闹钟+农历+整点报时

1. 硬件设计 硬件是我自己设计的一个通用的51单片机开发平台&#xff0c;可以根据需要自行焊接模块&#xff0c;这是用立创EDA画的一个双层PCB板&#xff0c;所以模块都是插针式&#xff0c;不是表贴的。电路原理图在文末的链接里&#xff0c;PCB图暂时不选择开源。 B站上传的…

颠覆AI界限!o3-mini与DeepSeek V3的巅峰对决

性能之战&#xff1a;谁才是AI推理的王者&#xff1f; 在AI技术飞速发展的今天&#xff0c;OpenAI最新发布的o3-mini模型如同一颗新星&#xff0c;闪耀在AIGC的天空中。它不仅带来了惊人的性能提升&#xff0c;还具备了多项用户友好的功能。与此同时&#xff0c;DeepSeek V3也…

Spring Boot项目如何使用MyBatis实现分页查询

写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油&#xff0c;冲鸭&#x…

PDCA 循环法

目录 循环周而复始大环套小环阶梯式上升 如何从 0 开始搭建 PDCA应用场景示例一、健身 APP 拉新活动策划第一阶段&#xff1a;制定详细的活动计划与方案第二阶段&#xff1a;活动执行阶段第三阶段&#xff1a;活动效果监控阶段第四阶段&#xff1a;活动复盘阶段 二、员工培训三…

深入理解Spring事务管理

一、事务基础概念 1.1 什么是事务&#xff1f; 事务&#xff08;Transaction&#xff09;是数据库操作的最小工作单元&#xff0c;具有ACID四大特性&#xff1a; 原子性&#xff08;Atomicity&#xff09;&#xff1a;事务中的操作要么全部成功&#xff0c;要么全部失败 一致…

【Linux-网络】初识计算机网络 Socket套接字 TCP/UDP协议(包含Socket编程实战)

&#x1f3ac; 个人主页&#xff1a;谁在夜里看海. &#x1f4d6; 个人专栏&#xff1a;《C系列》《Linux系列》《算法系列》 ⛰️ 道阻且长&#xff0c;行则将至 目录 &#x1f4da;一、初识计算机网络 &#x1f4d6; 背景 &#x1f4d6; 网络协议 &#x1f516;OSI七层…

2 MapReduce

2 MapReduce 1. MapReduce 介绍1.1 MapReduce 设计构思 2. MapReduce 编程规范3. Mapper以及Reducer抽象类介绍1.Mapper抽象类的基本介绍2.Reducer抽象类基本介绍 4. WordCount示例编写5. MapReduce程序运行模式6. MapReduce的运行机制详解6.1 MapTask 工作机制6.2 ReduceTask …

【Rust自学】15.5. Rc<T>:引用计数智能指针与共享所有权

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 15.5.1. 什么是Rc<T> 所有权在大部分情况下都是清晰的。对于一个给定的值&#xff0c;程序员可以准确地推断出哪个变量拥有它。 …

LabVIEW微位移平台位移控制系统

本文介绍了基于LabVIEW的微位移平台位移控制系统的研究。通过设计一个闭环控制系统&#xff0c;针对微位移平台的通信驱动问题进行了解决&#xff0c;并提出了一种LabVIEW的应用方案&#xff0c;用于监控和控制微位移平台的位移&#xff0c;从而提高系统的精度和稳定性。 项目背…

关于matlab中rotm2eul的注释错误问题

在这里&#xff0c;写的是默认的旋转矩阵的顺序为‘ZYX’&#xff0c;对应的旋转轴的顺序为Z、Y、X。 包括网页上写的是 但是实际上&#xff0c;在实际的应用中&#xff0c;旋转的顺序应该是&#xff0c;X、Y、Z轴的顺序 即RRzRyRx 但是在矩阵运算中&#xff0c;由于YRzRyRx*X…

前端 | 深入理解Promise

1. 引言 JavaScript 是一种单线程语言&#xff0c;这意味着它一次仅能执行一个任务。为了处理异步操作&#xff0c;JavaScript 提供了回调函数&#xff0c;但是随着项目处理并发任务的增加&#xff0c;回调地狱 (Callback Hell) 使异步代码很难维护。为此&#xff0c;ES6带来了…