概述
文章目录
- 强引用
- 软引用
- 弱引用
- 虚拟引用
- 引用队列
- ReferenceQueue
- Cleaner类
强引用
两种都属于强引用,强引用在开发中最常用,只要存在对对象的引用就不会被GC回收
// 我们自己定义的实体通过直接new的形式都是强引用
Object obj = new Object();
软引用
一般对象不会被回收,当应用内存将要被耗尽的时候–>即将要发生OOM时,垃圾处理器就会把它回收,可以防止因OOM导致应用宕机不可用。
SoftReference<String> ref = new SoftReference<String>("aaa");
弱引用
当根引用失效时,下次触发GC时会被回收,回收完即便根引用重新赋值也不会重新建立引用
在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
WeakReference<String> ref = new WeakReference<String>("aaa");
示例
String str=new String("333");
WeakReference<String>ref=newWeakReference<>(str);
System.out.println(ref.get());
str=null;
System.out.println(ref.get());
System.gc(); // 手动触发一次gc
System.out.println(ref.get());
结果: 333 333 null
虚拟引用
虚拟引用非真实引用,不影响正常垃圾回收
当垃圾回收器决定对PhantomReference对象进行回收时,会将其插入ReferenceQueue中。
PhantomReference.class
public class PhantomReference<T> extends Reference<T> {
public T get() {
return null;
}
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
对一个对象而言,这个引用形同虚设,有和没有一样。虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程,在对象被收集器回收时收到一个系统通知。 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,将这个虚引用加入引用队列,在其关联的虚引用出队前,不会彻底销毁该对象。
虚拟引用通常用于与引用队列ReferenceQueue
同时使用
引用队列
ReferenceQueue
ReferenceQueue可以与SoftReference/WeakReference/PhantomReference配合使用
refrenceQueue
本质上是一个链表,使用synchronized
保证线程安全性,维护了volatile
修饰的head
对象,表示当前头部对象,并且维护了当前队列数量
看一下线程安全问题是如何保证的
static private class Lock { };
private Lock lock = new Lock();
可以看到此处使用了一个全局锁对象,这是个空对象,只单纯作为锁对象存在,其核心只是锁住了remove
方法和enqueue
方法(加入队列和移除队列)
synchronized(lock){
// ...
}
当我们调用remove
方法时会调用Object.wait
方法阻塞,触发gc时enqueue
会调用notifyAll通知线程,并非cas操作,因此对性能影响并不大
下面模拟一个场景,当我们对象被gc回收时,则移除map中的引用key
public class Test {
private static ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>();
private static int _1M = 1024 * 1024;
public static void main(String[] args) throws InterruptedException {
final Map<Object, MybObject> map = new HashMap<>();
Thread thread = new Thread(() -> {
try {
int n = 0;
MybObject k;
while (null != (k = (MybObject) referenceQueue.remove())) {
// 每次触发gc都会进来一次
System.out.println("gc:" + (++n));
map.remove(k.key);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.setDaemon(true);
thread.start();
// 创建1000个对象放入map中
for (int i = 0; i < 1000; i++) {
byte[] bytesKey = new byte[_1M];
byte[] bytesValue = new byte[_1M];
map.put(bytesKey, new MybObject(bytesKey, bytesValue, referenceQueue));
}
Thread.currentThread().join();
}
static class MybObject extends PhantomReference<byte[]> {
private Object key;
MybObject(Object key, byte[] referent, ReferenceQueue<? super byte[]> q) {
super(referent, q);
this.key = key;
}
}
}
运行结果
gc:1
gc:2
...
gc:792
gc:793
gc:794
可以看到这里回收到794就没有回收了,剩下206个对象还在堆内存中没有被释放
注: 该结果并非在所有电脑下都一样,和电脑,配置,jvm参数都有关,每次执行结果也不尽相同
Cleaner类
通过下图可以看到Cleaner继承PhantomRefrence虚拟引用类,其本质上也还是一个Refrence。
ReferenceHandler会不停的从pending链表中取出引用对象,这个引用对象类型可能是任意一种,当引用对象为Cleaner时,直接调用Cleaner的clean方法就结束了。
使用方式也很简单,传入对象实例和runnable线程,当对象被回收时则会触发runnable的run方法
Cleaner.create(this, new Runnable() {
@Override
public void run() {
}
});
开发中大部分情况下用的都是强引用,某些特殊的业务场景下,开发模式下也会用到其他引用类型,建议一定要仔细阅读本文,酌情使用
最后
以上就是执着含羞草为你收集整理的JAVA中的引用类型,强引用软引用弱引用虚拟引用的全部内容,希望文章能够帮你解决JAVA中的引用类型,强引用软引用弱引用虚拟引用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复