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。

### Java 引用类型的详细介绍 Java 是一种面向对象的编程语言,在其运行时环境中,引用类型扮演着重要角色。它们不仅决定了如何管理对象生命周期,还影响垃圾回收机制的行为。 #### 1. **强引用 (Strong Reference)** 强引用是 Java 中最常见的一种引用类型,默认情况下所有的对象都通过这种方式创建和维护。当一个对象具有至少一个强引用时,即使 JVM 的堆内存不足也不会触发垃圾回收器对该对象进行清理[^2]。例如: ```java String str = new String("Hello"); ``` 上述代码中 `str` 就是一个典型的强引用变量,它指向新创建的字符串对象 `"Hello"`。只要 `str` 存在于作用域内,JVM 不会释放该对象所占用的内存资源。 #### 2. **软引用 (Soft Reference)** 软引用适用于那些可以被回收但仍希望尽可能长时间保留的对象场景,比如缓存系统。与强引用不同的是,当 JVM 面临内存压力时,可能会主动回收由软引使用的对象来腾出空间[^3]。下面是如何定义软引用的一个例子: ```java import java.lang.ref.SoftReference; // 创建实际对象 Object obj = new Object(); // 建立软引用关联 SoftReference<Object> softRef = new SoftReference<>(obj); // 清除原始强引用 obj = null; ``` 在此之后,如果发生内存紧张状况,则可能丢弃掉 `softRef` 所指代的内容;但如果当前仍有足够的可用存储容量的话,那么这些数据将继续存活直到必要为止。 #### 3. **弱引用 (Weak Reference)** 相比起软引用更加脆弱——一旦发现任何持有它的实例不再具备其他形式的有效连接(即仅有弱引用存在),就会立刻成为候选清除目标之一。因此通常应用于某些临时性的辅助结构上,如映射表实现等场合下减少不必要的长期占有宝贵物理地址的情况的发生率: ```java import java.lang.ref.WeakReference; // 初始化实体项目 Integer number = 42; // 构造弱化版本链接关系 WeakReference<Integer> weakNum = new WeakReference<>(number); // 解绑原初绑定路径 number = null; ``` 这里需要注意的是,尽管我们已经断开了直接访问途径 (`number=null`) ,但由于仍然存在着间接方式能够触及到数值本身(`weakNum.get()`),所以严格意义上讲此时还没有完全消除对其依赖性,只有等到GC执行完毕后才能彻底销毁对应记录. #### 4. **虚引用 (Phantom Reference)** 最后介绍的是幻影/幽灵级别最低级别的引用类别—虚引用(Phantom Reference)[^3].与其他几种情况有所不同之处在于,无法经由此种手段重新获得已分配出去但即将消失不见的目标副本信息。换句话说也就是即便设置了这样的标记也无济于事因为根本不可能再读取回来什么有用的东西了除非借助专门设计好的队列机制配合工作才行: ```java import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; public class PhantomExample { public static void main(String[] args) throws InterruptedException { Object phantomObj = new Object(); ReferenceQueue<Object> refQueue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(phantomObj ,refQueue); phantomObj =null ; System.gc(); // 提醒 GC 进程启动 Thread.sleep(100); // 给予足够时间让 GC 完成操作 if(refQueue.poll() != null){ System.out.println("The object has been collected."); } } } ``` 在这个案例里头,当我们将唯一存在的强引用设置为空以后紧接着调用了全局范围内的强制清扫命令(System.gc()),随后稍作等待确保整个过程顺利完成。最终确认我们的测试对象确实已经被成功处理掉了才会打印相应消息出来表明一切正常运作当中。 --- ### 总结 综上所述,Java提供了四种主要的引用类型:强引用、软引用、弱引用以及虚引用。每种都有各自特定用途及特点,合理选用可以帮助开发者构建更高效稳定的应用程序架构体系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值