Java中的四种Reference

本文详细介绍了Java中的四种引用类型:强引用、软引用、弱引用和虚引用。通过示例展示了它们在内存管理和垃圾收集中的行为,特别是如何影响对象的生命周期。软引用在内存溢出前会被回收,而弱引用的对象在下一次GC时会被回收。虚引用不能获取到对象实例,主要用于对象被回收前的资源清理。文中还提到了WeakHashMap和DirectByteBuffer的清理机制,以及在Java NIO中的应用。
摘要由CSDN通过智能技术生成

首先要大致了解 Java 的几种引用类型。如下图所示,JDK 1.2 之后新增了 Reference 的概念,给开发人员提供了与 GC 交互的一种渠道。

《深入理解 Java 虚拟机》中对于几种引用类型做了简要的描述:

强引用(_Strongly Reference_ )是最传统的「引用」的定义,是指在程序代码中普遍存在的引用赋值,即类似 Object obj = new Ojbect() 这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。

软引用(_Soft Reference_)是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在 JDK 1.2 之后提供了 SoftReference 来实现软引用。

弱引用(_Weak Reference_)也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在 JDK 1.2 之后提供了 WeakReference 来实现弱引用。

虚引用(_Phantom Reference_)也被称为「幽灵引用」或者「幻影引用」,它是最弱的一种引用关系。一个对象是否有虚引用存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在 JDK 1.2 之后提供了 PhantomReference 来实现虚引用。

书中的介绍较为概括,并且没有提供相关的示例,当时第一次看这段文字时并没有搞清楚这几个引用的含义和用法。为了更好地理解,下面将通过几个示例进行分析介绍。

Reference

Reference 中有两个重要的字段:

private T referent;         /* Treated specially by GC */

volatile ReferenceQueue<? super T> queue;

其中, referent 就是指向实际对象的引用,注释中也强调了这个引用会被 GC 特殊对待 ;而queue 是与这个 Reference 关联的队列,GC 会在某些特殊阶段将当前 Reference 放入这个队列中,下面通过实例进行分析。

下面是一个最简单的示例,描述 Reference 的基本结构,这里以 SoftReference 为例:

private static ReferenceQueue queue = new ReferenceQueue();

public void softReference() {
    Object obj = new Object();
    SoftReference<Object> soft = new SoftReference<>(obj, queue);
}

图中,彩色部分为 GC Roots,其中 local variables 为虚拟机栈中的 局部变量表 ,而 metaspace 为 元空间 。实线表示强引用,虚线表示弱引用。局部变量表中的 soft 强引用 指向了堆中的 SoftReference 实例对象, ojb 强引用 指向了 Object 实例对象。而元空间中有个名为 queue 的 强引用 指向了堆中的 ReferenceQueue 对象。

SoftReference 需要在堆中单独使用一块堆内存记录一个软引用对象,该对象的 referent 软指向 (这里的软指向就是指上文中的 GC 特殊对待 ,本质上来说它还是一个强引用,在调用 Reference 的 get 方法时,会返回该强引用,该强引用可以赋值给 GC Roots 或其他可达的强引用,可以用这种方式为对象「续命」)实际的 Object 实例对象,而 queue 强引用 指向了堆中的ReferenceQueue 实例对象。

现在将图中的紫色强引用断开,如下所示:

private static ReferenceQueue queue = new ReferenceQueue();

public void softReference() {
    Object obj = new Object();
    SoftReference<Object> soft = new SoftReference<>(obj, queue);
    // 断开强引用
    ojb = null;
}

此时,对于堆中的 Ojbect 实例对象来说,仅仅剩下了一个 referent 软引用 指向它,某些文章中称之为 软可达对象 (_softly reachable object_),这个对象就满足了 GC 的特殊对待要求,当内存溢出时,会将其占用的堆空间回收,并将 soft 指向的 SoftReference 实例对象放入其 queue 关联的 ReferenceQueue 实例对象中。

SoftReference 示例

SoftReference 被回收需要的前提是内存溢出,因此需要先设定虚拟机参数:

/**
 * -Xms10M -Xmx10M -XX:+PrintGC 
 */
public class SoftReferenceDemo {
    private static List<SoftReference<Data>> softReferences = new ArrayList<>();
    private static ReferenceQueue queue = new ReferenceQueue();

    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    public static void main(String[] args) throws InterruptedException {
        new SoftReferenceHandler().start();
        softReferenceTest();
    }

    private static void softReferenceTest() {
        int i = 0;
        while (i < 8) {
            Data data = new Data();
            SoftReference<Data> softData = new SoftReference<>(data, queue);
            System.out.printf("Add Data%s's SoftReference to list\n", data.hashCode());
            softReferences.add(softData);
            data = null;
            i++;
        }

        for (int j = 0; j < softReferences.size(); j++) {
            System.out.printf("softReferences[%d] real Object is: %s\n", j, softReferences.get(j).get() == null ?
                    "null" : "Data" + softReferences.get(j).get().hashCode());
        }

        countDownLatch.countDown();
    }

    private static class SoftReferenceHandler extends Thread {

        @Override
        public void run() {
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Reference reference;
            while (true) {
                if ((reference = queue.poll()) != null) {
                    System.out.printf("SoftReference%s has been unreachable\n", reference.hashCode());
                }
            }
        }
    }
}

class Data {
    private static final int _1M = 1024 * 1024;
    private final byte[] data = new byte[_1M];
}

上述代码定义了一个存放 SoftReference 的列表 ArrayList ( softReferences )以及与所有SoftReference 相关联的引用队列 ReferenceQueue ( queue )。 CountDownLatch 的使用是为了结果的打印顺序比较直观。

主线程中 softReferenceTest 方法的第一个 while 循环往 softReferences 中添加 Data 的 SoftReference ,同时将该 Data 的强引用断开,使得 Data 对象只有 softReferences 中的软引用。当堆中的空间无法容纳 Data 时(示例中设定了虚拟机相关参数,固定堆大小为 10M),会触发 OOM,而对软引用来说,在触发 OOM 之前会再进行一次 GC,对软引用的对象进行清理,而这些被清理了实际对象的软引用会被 GC 放到指定的队列 queue 中。第二个 for 循环打印 GC 完成之后 softReferences 中所有软引用的实际对象(即 referent )。这段代码输出结果大致如下:加入 7 个 Data 后,再添加第 8 个对象时,堆中剩余空间不足,触发了 GC,并将前 7 个 Data 实例对象进行了回收,腾出空间后,将第 8 个对象实例化并加入软引用列表。

Add Data1956725890's SoftReference to list
Add Data692404036's SoftReference to list
Add Data1554874502's SoftReference to list
Add Data1846274136's SoftReference to list
Add Data1639705018's SoftReference to list
Add Data1627674070's SoftReference to list
Add Data1360875712's SoftReference to list
[GC (Allocation Failure) -- 8537K->8705K(9728K), 0.0010698 secs]
[Full GC (Ergonomics)  8705K->7971K(9728K), 0.0055837 secs]
[GC (Allocation Failure) -- 7971K->8011K(9728K), 0.0009023 secs]
[Full GC (Allocation Failure)  8011K->759K(9728K), 0.0136402 secs]
Add Data1625635731's SoftReference to list
softReferences[0] real Object is: null
softReferences[1] real Object is: null
softReferences[2] real Object is: null
softRefere
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值