Java幽灵引用的作用

找实习的时候,面试大摩,就遇到了这个问题,当时真不该跟面试官交流这个内容的。

垃圾收集过程中,对象的可触及状态改变的时候,可以把引用对象和引用队列关联起来【这里说的关联,是说垃圾收集器会把要回收的对象添加到引用队列ReferenceQueue】,这样在可触及性发生变化的时候得到“通知”。

当垃圾收集器对加入队列的对象改变可触及性的时候,就可以收到异步通知了。

看下面的代码:

package static_;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;

public class Test {
	public static boolean isRun = true;

	@SuppressWarnings("static-access")
	public static void main(String[] args) throws Exception {
		String abc = new String("abc");
		System.out.println(abc.getClass() + "@" + abc.hashCode());
		final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
		new Thread() {
			public void run() {
				while (isRun) {
					Object obj = referenceQueue.poll();
					if (obj != null) {
						try {
							Field rereferent = Reference.class
									.getDeclaredField("referent");
							rereferent.setAccessible(true);
							Object result = rereferent.get(obj);
							System.out.println("gc will collect:"
									+ result.getClass() + "@"
									+ result.hashCode() + "\t"
									+ (String) result);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
			}
		}.start();
		PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,
				referenceQueue);
		abc = null;
		Thread.currentThread().sleep(3000);
		System.gc();
		Thread.currentThread().sleep(3000);
		isRun = false;
	}
}

我们用一个线程检测referenceQueue里面是不是有内容,如果有内容,打印出来queue里面的内容。

从这个例子中,我们可以看出来,虚引用的作用是,我们可以声明虚引用来引用我们感兴趣的对象,在gc要回收的时候,gc收集器会把这个对象添加到referenceQueue,这样我们如果检测到referenceQueue中有我们感兴趣的对象的时候,说明gc将要回收这个对象了此时我们可以在gc回收之前做一些其他事情,比如记录些日志什么的

----------------------------------------------分割----------------------------------------------------

感谢蓝大牛分享下面的例子。

在java中,finalize函数本来是设计用来在对象被回收的时候来做一些操作的(类似C++的析构函数)。但是对象被GC什么时候回收的时间,却是不固定的,这样finalize函数很尴尬。虚引用可以用来解决这个问题。

在创建虚引用的时候必须传入一个引用队列。在一个对象的finalize函数被调用之后,这个对象的幽灵引用会被加入到引用队列中。通过检查队列的内容就知道对象是不是要准备被回收了。

幽灵引用的使用并不多见,主要是实现细粒度的内存控制。比如下面代码实现一个缓存。程序在确认原来的对象要被回收之后,才申请内存创建新的缓存。

在上面的代码中,每次申请新的缓存的时候,都要确保之前的字节数组被成功回收。引用队列的remove方法会阻塞直到虚引用被加入到引用队列中。【只有对象在内存中被移除之后才会进入引用队列中】【?这里有点不太确定。后续补发】

不过注意,这种方式可能会导致gc次数过多,程序吞吐量下降。

另外注意,system.gc调用仅仅是建议虚拟机进行回收,并不一定马上会进行gc。

<think>首先,用户的问题是:“我想了解Java虚引用的相关信息 请问Java虚引用介绍”。这基于提供的引用内容。 系统指令要求: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1] 参考的引用内容: - [^1]: 虚引用是最弱的引用类型,不影响对象生命周期,无法通过虚引用获得对象实例。 - [^2]: 虚引用必须和ReferenceQueue联合使用,监控GC活动;示例代码演示了如何使用。 - [^3]: 另一个示例代码,展示了虚引用的行为。 - [^4]: 虚引用用于跟踪对象被垃圾回收器回收的活动;必须和引用队列联合使用;在对象回收前,虚引用被加入队列。 - [^5]: 涉及软引用示例,但用户问题是关于虚引用,所以主要关注虚引用部分。 回答结构: - 作为AI助手,我需要逐步解释Java虚引用。 - 确保回答真实可靠,基于提供的引用。 - 结尾生成相关问题。 大纲回答: 1. **介绍虚引用的基本概念**:解释什么是Java虚引用,它是最弱的引用类型,不决定对象生命周期,无法获取对象实例。 2. **虚引用的特点**:必须与ReferenceQueue联合使用;用于监控GC活动。 3. **虚引用的用途**:主要用来跟踪对象何时被垃圾回收。 4. **代码示例解释**:简要解释引用中的代码示例,说明虚引用如何工作。 5. **引用队列的作用**:当对象被回收时,虚引用被加入队列,开发者可以检测到。 6. **与软引用和弱引用的区别**:简要对比,强调虚引用的特殊性。 7. **生成相关问题**:结尾添加相关问题。 使用中文回答: - 整个回答都用中文。 引用标识: - 当引用具体内容时,在段落末尾添加[^n]。 潜在数学表达式: - 用户问题是关于Java的,可能没有数学内容。系统指令说“所有行内数学表达式”,但这里可能不适用。如果有数字或简单变量,可以用$...$,但Java引用不涉及复杂数学。专注于Java概念。 生成相关问题: - 结尾添加§§相关问题§§部分。 最终回答结构: - 开头:介绍虚引用。 - 主体:详细解释,包括代码示例解读。 - 结尾:总结。 - 相关问题部分。 具体内容: - 从引用[^1]:虚引用是最弱的引用类型,不影响生命周期。 - 从引用[^2]和[^3]:示例代码,展示虚引用如何与队列一起使用。 - 从引用[^4]:解释用途和机制。 - 从引用[^5]:包括软引用,但用户关心虚引用,所以略过或对比。 确保可靠:基于给定的引用内容,不要添加外部知识。 </think>### Java虚引用介绍 Java中的虚引用(PhantomReference)是一种特殊的引用类型,它在垃圾回收机制中扮演独特角色。虚引用是最弱的引用类型(弱于软引用和弱引用),它不会影响对象的生命周期——也就是说,即使一个对象只有虚引用存在,垃圾回收器(GC)仍然可以回收它[^1]。更重要的是,**无法通过虚引用直接获取对象实例**(调用`get()`方法始终返回`null`),这与强引用、软引用或弱引用有本质区别[^1]。 #### 虚引用的核心特点 1. **必须与ReferenceQueue联合使用**:虚引用不能单独使用,必须绑定一个引用队列(`ReferenceQueue`)。当对象被GC回收时,虚引用本身会被添加到关联的队列中,开发者可以通过轮询队列来检测对象回收事件[^2][^4]。 2. **用途:监控垃圾回收活动**:虚引用的主要作用是跟踪对象的回收过程,常用于实现资源清理机制,如监控数据库连接池或文件句柄的生命周期[^4]。例如: - 开发者可以创建一个虚引用指向对象,并监听队列:当虚引用出现在队列中时,意味着对象已被回收,可以执行后续清理操作。 3. **回收机制**:对象被回收时,GC会先执行`finalize()`方法(如果存在),然后将虚引用入队。这发生在对象内存被释放之前,为开发者提供一个“通知”时机[^4]。 #### 代码示例解析 以下是一个简化示例,基于引用[^3]中的代码,展示虚引用的典型用法: ```java import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; public class PhantomReferenceDemo { public static void main(String[] args) { Object obj = new Object(); // 创建一个对象 ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(obj, queue); System.out.println("GC前:obj=" + obj + ", phantomRef.get()=" + phantomRef.get() + ", queue=" + queue.poll()); // 输出:obj有值,但phantomRef.get()始终为null obj = null; // 去除强引用,对象可被回收 System.gc(); // 触发GC try { Thread.sleep(500); // 确保GC执行完毕 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("GC后:obj=" + obj + ", phantomRef.get()=" + phantomRef.get() + ", queue=" + queue.poll()); // 输出:obj为null,phantomRef.get()为null,但queue.poll()返回虚引用,确认对象被回收 } } ``` - **关键点**: - `phantomRef.get()`永远返回`null`,证明虚引用无法获取对象实例[^3]。 - GC后,队列中会出现虚引用,开发者通过`queue.poll()`检测回收事件,用于日志记录或资源释放[^2][^4]。 #### 与其他引用的对比 Java有四种引用类型: - **强引用(Strong Reference)**:最常见,如`Object obj = new Object()`,对象不会被GC回收。 - **软引用(Soft Reference)**:内存不足时触发回收,适合缓存。 - **弱引用(Weak Reference)**:下一次GC时回收。 - **虚引用**:只用于跟踪回收,不影响回收时机,也无法获取对象[^4][^5]。 虚引用与软/弱引用的最大区别在于它必须结合队列使用,且完全“透明”于对象的生命周期管理[^1]。 #### 应用场景 虚引用常用于高级场景: - **资源泄漏检测**:监控对象(如文件流或网络连接)何时被回收,确保及时关闭资源[^4]。 - **内存敏感系统**:在需要精确控制对象回收事件的框架中(如某些缓存库),通过队列机制实现回调逻辑[^2]。 - **性能优化工具**:结合`ReferenceQueue`构建GC监控工具,帮助开发者分析内存使用模式[^4]。 总结来说,Java虚引用是一种“幽灵般”的引用机制,它不干预对象回收,但通过队列提供回收事件的钩子(hook),适用于需要精细控制资源管理的场景[^1][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值