Java Reference概念整理

本文深入探讨 Java 中引用类型的原理与应用,包括强引用、弱引用、软引用和幻影引用的概念、作用与实例代码解析,以及如何利用 ReferenceQueue 实现事件回调。
摘要由CSDN通过智能技术生成

Strong Reference, 强引用,即java标准的引用方式,表示GC从 Root Set 开始向下扫描,可以找到对应的 Strong Reference。

Referent,被包装为 Weak, Soft, Phantom Reference的对象引用称之为 referent。后面的内容会多次提到这个名词。

Weak Reference, 弱引用。当一个referent,在运行时没有同时被强,软引用,只被Weak Reference自身引用,且Weak Reference从 Root Set 可达,则该referent会被GC回收。

WR的作用,一般是为referent提供一个被回收的凭据,结合ReferenceQueue可以让程序在第一时间得到referent被回收的事件,从而做一些额外的clean操作。(如果对ReferenceQueue作用和回调感兴趣,可以先看最下面的 ReferenceQueue 简介)

Soft Reference, 软引用。它是除strong外,生命周期最长的一种 Reference,只有当JVM Heap中充满Strong References, Full GC无法为heap腾出更多空间而即将抛出OOM时,SoftReferences会被GC回收。

SR的作用一般是用作不限大小的 cache(无需remove)。
比如将其 Soft Reference 无限地放入 hashmap 中,而不关心hashmap内的对象数量是否会撑爆heap,也不需要手动 remove。
当Heap容量达到OOM触发条件时,VM会自动回收该map里的所有SoftReferences.

Phanton Reference, 是一种特殊的Reference,正如他的名字所表达的,幻影引用,他可以像幻影一样附着在referent上。
当GC在遍历引用关系时,如果发现被phantom reference包装过的referent不存在strong, weak, soft引用时(就是除phantom外没有任何引用,幻影的由来),GC会将 phantom reference 放入 Reference queue。以便程序在另一边通过queue的remove/poll方法,感知referent被GC回收的事件。(如果对ReferenceQueue作用和回调感兴趣,可以先看最下面 ReferenceQueue 简介)

另外,我们知道,GC在回收对象前会先调用对象自身的finalize()方法,如果它有实现的话,然后再清掉内存。而Phantom Reference的回调(enqueue)是在对象的finalize后,回收前触发。这跟 WeakReference不一样。WR是在回收后才通知的。在这个特殊的阶段可以做一些特殊的clean操作。

为什么 Phantom Reference 的get总是返回null?
因为phantom reference想做到幻影(除自身外,不跟其他任何引用有关联),所以不允许程序能通过自身的get方法得到referent,而破坏幻影的初衷。

实例代码

package com.kenwublog.reference;
 
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.HashMap;
 
public class PhantomReferenceTest {
 
	public static void main(String[] args) {
		ReferenceQueue referenceQueue = new ReferenceQueue();
		Object object = new Object() {
			public String toString() {
				return "Referenced Object";
			}
		};
 
		Object data = new Object() {
			public String toString() {
				return "Data";
			}
		};
 
		HashMap map = new HashMap();
		Reference reference = null;
		System.out.println("Testing PhantomReference.");
		reference = new PhantomReference(object, referenceQueue);
 
		map.put(reference, data);
 
		System.out.println(reference.get()); // null
		System.out.println(map.get(reference)); // Data
		System.out.println(reference.isEnqueued()); // false
 
		System.gc();
		System.out.println(reference.get()); // null
		System.out.println(map.get(reference)); // Data
		System.out.println(reference.isEnqueued()); // false
 
		object = null;
		data = null;
 
		System.gc();
		System.out.println(reference.get()); // null
		System.out.println(map.get(reference)); // Data
		System.out.println(reference.isEnqueued()); // true, because object has been reclaimed.
	}
 
}

ReferenceQueue,一种当 weak, soft, phantom的referent被GC回收后,提供事件回调的接口。需要在实例化三大reference时,通过构造函数传入,phantom reference是强制需要传入的,weak和soft可不传。

回调过程:

GC回收referent后(phantom是在回收前,finalize后),将reference enqueue到RQ中,程序通过调用RQ的remove方法来感知reference被GC回收的事件。
remove方法是阻塞的,当没有referent被回收时(GC未调用enqueue),remove方法会一直挂起线程,当有referent被回收时,该方法返回 referent对应的reference对象。
同样,RQ也提供了一个非阻塞的方法 poll,但这样就做不到实时回调了。

实例

package com.kenwublog.reference;
 
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
 
public class ReferenceQueueTest {
 
	public static void main(String[] args) {
		final ReferenceQueue q = new ReferenceQueue();
		String str = new String("AK47");
		WeakReference wr = new WeakReference(str, q);
 
		Thread t = new Thread(){
			@Override
			public void run() {
				try {
					Reference reference = q.remove();
					System.out.println(reference + " event fired.");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		t.setDaemon(true);
		t.start();
		System.out.println("Reference Queue is listening.");
 
		str = null; // clear strong reference
		System.out.println("Ready to gc");
		System.gc();
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("wr.get: " + wr.get());
	}
 
}

参考资料

http://mindprod.com/jgloss/phantom.html (References的目的,在GC的哪种阶段触发对比表格)
http://www.pawlan.com/monica/articles/refobjs/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值