Java的幽灵引用定义与使用场景

本文详细介绍了Java中的四种引用类型:StrongReference、SoftReference、WeakReference和PhantomReference,并探讨了它们与垃圾回收机制之间的关系及应用场景。

Java 中一共有 4 种类型的引用 : StrongReference、 SoftReference、 WeakReference 以及 PhantomReference (传说中的幽灵引用 呵呵),

  这 4 种类型的引用与 GC 有着密切的关系,  让我们逐一来看它们的定义和使用场景 :

  1. Strong Reference

  StrongReference 是 Java 的默认引用实现,  它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时 GC 执行后将会被回收

  Java代码

  1.@Test

  2.public void strongReference() {

  3.    Object referent = new Object();

  4.

  5.    /**

  6.     * 通过赋值创建 StrongReference

  7.     */

  8.    Object strongReference = referent;

  9.

  10.    assertSame(referent, strongReference);

  11.

  12.    referent = null;

  13.    System.gc();

  14.

  15.    /**

  16.     * StrongReference 在 GC 后不会被回收

  17.     */

  18.    assertNotNull(strongReference);

  19.}

  2. WeakReference & WeakHashMap

  WeakReference, 顾名思义,  是一个弱引用,  当所引用的对象在 JVM 内不再有强引用时, GC 后 weak reference 将会被自动回收

  Java代码

  1.@Test

  2.public void weakReference() {

  3.    Object referent = new Object();

  4.    WeakReference<Object> weakRerference = new WeakReference<Object>(referent);

  5.

  6.    assertSame(referent, weakRerference.get());

  7.

  8.    referent = null;

  9.    System.gc();

  10.

  11.    /**

  12.     * 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回收

  13.     */

  14.    assertNull(weakRerference.get());

  15.}

  WeakHashMap 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry

  Java代码

  1.@Test

  2.public void weakHashMap() throws InterruptedException {

  3.    Map<Object, Object> weakHashMap = new WeakHashMap<Object, Object>();

  4.    Object key = new Object();

  5.    Object value = new Object();

  6.    weakHashMap.put(key, value);

  7.

  8.    assertTrue(weakHashMap.containsValue(value));

  9.

  10.    key = null;

  11.    System.gc();

  12.

  13.    /**

  14.     * 等待无效 entries 进入 ReferenceQueue 以便下一次调用 getTable 时被清理

  15.     */

  16.    Thread.sleep(1000);

  17.

  18.    /**

  19.     * 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry

  20.     */

  21.    assertFalse(weakHashMap.containsValue(value));

  22.}

  3. SoftReference

  SoftReference 于 WeakReference 的特性基本一致, 最大的区别在于 SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用

  Java代码

  1.@Test

  2.public void softReference() {

  3.    Object referent = new Object();

  4.    SoftReference<Object> softRerference = new SoftReference<Object>(referent);

  5.

  6.    assertNotNull(softRerference.get());

  7.

  8.    referent = null;

  9.    System.gc();

  10.

  11.    /**

  12.     *  soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用

  13.     */

  14.    assertNotNull(softRerference.get());

  15.}

  4. PhantomReference

  作为本文主角, Phantom Reference(幽灵引用) 与 WeakReference 和 SoftReference 有很大的不同,  因为它的 get() 方法永远返回 null, 这也正是它名字的由来

  Java代码

  1.@Test

  2.public void phantomReferenceAlwaysNull() {

  3.    Object referent = new Object();

  4.    PhantomReference<Object> phantomReference = new PhantomReference<Object>(referent, new ReferenceQueue<Object>());

  5.

  6.    /**

  7.     * phantom reference 的 get 方法永远返回 null

  8.     */

  9.    assertNull(phantomReference.get());

  10.}

  诸位可能要问, 一个永远返回 null 的 reference 要来何用,  请注意构造 PhantomReference 时的第二个参数 ReferenceQueue(事实上 WeakReference & SoftReference 也可以有这个参数),

  PhantomReference 唯一的用处就是跟踪 referent  何时被 enqueue 到 ReferenceQueue 中.

  5. RererenceQueue

  当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作.   将一个 ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个对象插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 entries.

  Java代码

  1.@Test

  2.public void referenceQueue() throws InterruptedException {

  3.    Object referent = new Object();

  4.    ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();

  5.    WeakReference<Object> weakReference = new WeakReference<Object>(referent, referenceQueue);

  6.

  7.    assertFalse(weakReference.isEnqueued());

  8.    Reference<? extends Object> polled = referenceQueue.poll();

  9.    assertNull(polled);

  10.

  11.    referent = null;

  12.    System.gc();

  13.

  14.    assertTrue(weakReference.isEnqueued());

  15.    Reference<? extends Object> removed = referenceQueue.remove();

  16.    assertNotNull(removed);

  17.}

  6.PhantomReference  vs WeakReference

  PhantomReference  有两个好处, 其一, 它可以让我们准确地知道对象何时被从内存中删除, 这个特性可以被用于一些特殊的需求中(例如 Distributed GC,  XWork 和 google-guice 中也使用 PhantomReference 做了一些清理性工作).

  其二, 它可以避免 finalization 带来的一些根本性问题, 上文提到 PhantomReference 的唯一作用就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中,  但是 WeakReference 也有对应的功能, 两者的区别到底在哪呢 ?

  这就要说到 Object 的 finalize 方法, 此方法将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用,  这将导致这一轮的 GC 无法回收这个对象并有可能

  引起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用,  也就不会出现上述问题,  当然这是一个很极端的例子, 一般不会出现.

  7. 对比

 

Soft vs Weak vs Phantom References
TypePurposeUseWhen GCedImplementing Class
Strong ReferenceAn ordinary reference. Keeps objects alive as long as they are referenced.normal reference.Any object not pointed to can be reclaimed.default
Soft ReferenceKeeps objects alive provided there’s enough memory.to keep objects alive even after clients have removed their references (memory-sensitive caches), in case clients start asking for them again by key.After a first gc pass, the JVM decides it still needs to reclaim more space.java.lang.ref.SoftReference
Weak ReferenceKeeps objects alive only while they’re in use (reachable) by clients.Containers that automatically delete objects no longer in use.After gc determines the object is only weakly reachablejava.lang.ref.WeakReference 
java.util.WeakHashMap
Phantom ReferenceLets you clean up after finalization but before the space is reclaimed (replaces or augments the use offinalize())Special clean up processingAfter finalization.java.lang.ref.PhantomReference

 

  8. 小结

  一般的应用程序不会涉及到 Reference 编程, 但是了解这些知识会对理解 GC 的工作原理以及性能调优有一定帮助,   在实现一些基础性设施比如缓存时也可能会用到, 希望本文能有所帮助.

<think>我们参考了提供的引用资料,并整合了关于Java四种引用的信息。四种引用分别是:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。下面将逐一详细解释。 ### 1. 强引用(Strong Reference) 强引用是最常见的引用类型,我们平时使用的普通对象引用就是强引用。只要强引用存在,垃圾回收器就永远不会回收被引用的对象。 ```java Object obj = new Object(); // 这就是一个强引用 ``` - **特点**:强引用可以直接访问目标对象,且只要强引用存在,对象就不会被回收。 - **回收时机**:当对象没有任何强引用指向时,才会被垃圾回收器回收。 - **使用场景**:一般对象都是通过强引用来使用。 ### 2. 软引用(Soft Reference) 软引用用来描述一些还有用但非必需的对象。在系统将要发生内存溢出异常之前,会把这些对象列进回收范围进行回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。 ```java SoftReference<Object> softRef = new SoftReference<>(new Object()); ``` - **特点**:当内存不足时,垃圾回收器会回收软引用指向的对象。 - **回收时机**:在内存不足时,由垃圾回收器决定回收。 - **使用场景**:适用于实现内存敏感的缓存,如图片缓存、网页缓存等。软引用可以和一个引用队列(`ReferenceQueue`)联合使用,如果软引用所引用的对象被回收,这个软引用就会被加入引用队列。 ### 3. 弱引用(Weak Reference) 弱引用也是用来描述非必需对象的,但它的强度比软引用更弱。被弱引用关联的对象只能生存到下一次垃圾回收发生之前。 ```java WeakReference<Object> weakRef = new WeakReference<>(new Object()); ``` - **特点**:无论内存是否充足,只要发生垃圾回收,弱引用指向的对象就会被回收。 - **回收时机**:在垃圾回收时,一旦发现弱引用,就会回收其对象。 - **使用场景**:适用于弱键缓存(如`WeakHashMap`),防止内存泄漏。弱引用也可以和引用队列联合使用。 ### 4. 虚引用(Phantom Reference) 虚引用也称为“幽灵引用”或“幻象引用”,它是最弱的一种引用关系。虚引用完全不会影响对象的生命周期,也无法通过虚引用来取得一个对象实例。 ```java ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue); ``` - **特点**:虚引用无法获取对象实例,其存在意义在于跟踪对象被垃圾回收的状态。 - **回收时机**:虚引用指向的对象在垃圾回收时会被回收,但虚引用本身不会自动清除,需要配合引用队列使用。 - **使用场景**:主要用于跟踪对象被垃圾回收的活动,例如在对象被回收时收到一个系统通知(通过检查引用队列)。在Java NIO中,堆外内存(`DirectByteBuffer`)的清理就利用了虚引用机制。 ### 引用队列(ReferenceQueue) 软引用、弱引用和虚引用都可以引用队列一起使用。当引用所指向的对象被垃圾回收后,引用本身会被加入到引用队列中。这样,程序可以通过判断引用队列中是否有引用来了解被引用对象是否被回收。 --- §§相关问题§§ 1. 为什么软引用适合用于缓存场景? 2. 弱引用在`WeakHashMap`中是如何工作的? 3. 虚引用引用队列如何配合使用来完成堆外内存的清理? 4. 在开发中如何选择合适的引用类型? </think>### Java四种引用详解 在Java中,对象引用分为四种类型,帮助开发者灵活管理内存和对象生命周期。以下根据引用资料[^1][^2][^3][^4][^5]详细解析: --- #### 1. **强引用(Strong Reference)** - **定义**:最常见的引用类型,通过`new`创建的对象默认是强引用。 - **特性**: - 只要强引用存在,对象不会被垃圾回收(GC)。 - 内存不足时,JVM抛出`OutOfMemoryError`而非回收强引用对象。 - **示例**: ```java Object obj = new Object(); // 强引用 ``` - **使用场景**:常规对象创建(如业务逻辑核心对象)[^1][^2]。 --- #### 2. **软引用(Soft Reference)** - **定义**:通过`SoftReference`类实现,描述非必需但有用的对象。 - **特性**: - 内存充足时保留对象; - **内存不足时(GC前)**,被软引用指向的对象会被回收。 - 常配合`ReferenceQueue`跟踪回收状态[^3][^5]。 - **示例**: ```java SoftReference<String> softRef = new SoftReference<>("Cache Data"); System.out.println(softRef.get()); // 内存充足时可获取对象 ``` - **使用场景**:内存敏感的缓存(如图片、网页缓存)[^3][^5]。 --- #### 3. **弱引用(Weak Reference)** - **定义**:通过`WeakReference`类实现,强度低于软引用。 - **特性**: - 无论内存是否充足,**下一次GC时对象即被回收**。 - 常用于防止内存泄漏(如`WeakHashMap`的键)[^3][^4]。 - **示例**: ```java WeakReference<String> weakRef = new WeakReference<>("Temporary Data"); System.gc(); // GC后 weakRef.get() 返回 null ``` - **使用场景**:弱键缓存、监听器清理[^2][^4]。 --- #### 4. **虚引用(Phantom Reference)** - **定义**:通过`PhantomReference`类实现,最弱的引用类型。 - **特性**: - **无法通过虚引用获取对象**(`get()`始终返回`null`)。 - 对象被GC回收后,虚引用被加入`ReferenceQueue`,用于触发后续操作。 - **示例**: ```java ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue); // 对象回收后,phantomRef进入queue ``` - **使用场景**: - 堆外内存清理(如NIO的`DirectByteBuffer`)[^4]; - GC回收跟踪及定制化清理[^2][^4]。 --- ### 关键对比表 | 引用类型 | 回收时机 | 是否可获取对象 | 典型应用场景 | |------------|--------------------------|----------------|----------------------| | **强引用** | 永不回收(除非无引用) | 是 | 核心业务对象 | | **软引用** | 内存不足时 | 是 | 内存敏感缓存 | | **弱引用** | 下一次GC时 | 是 | 弱键缓存(如WeakHashMap) | | **虚引用** | 对象回收后通知 | 否 | 资源清理跟踪 | ### 引用队列(ReferenceQueue)的作用 所有非强引用均可关联`ReferenceQueue`: - 当引用指向的对象被GC回收后,引用本身会被加入队列; - 程序可通过轮询队列,执行后续清理逻辑(如关闭资源)[^3][^5]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值