我是靠谱客的博主 执着含羞草,最近开发中收集的这篇文章主要介绍JAVA中的引用类型,强引用软引用弱引用虚拟引用,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

    • 强引用
    • 软引用
    • 弱引用
    • 虚拟引用
    • 引用队列
      • 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。

image-20220424103149881

ReferenceHandler会不停的从pending链表中取出引用对象,这个引用对象类型可能是任意一种,当引用对象为Cleaner时,直接调用Cleaner的clean方法就结束了。

使用方式也很简单,传入对象实例和runnable线程,当对象被回收时则会触发runnable的run方法

Cleaner.create(this, new Runnable() {
@Override
public void run() {
}
});

开发中大部分情况下用的都是强引用,某些特殊的业务场景下,开发模式下也会用到其他引用类型,建议一定要仔细阅读本文,酌情使用

最后

以上就是执着含羞草为你收集整理的JAVA中的引用类型,强引用软引用弱引用虚拟引用的全部内容,希望文章能够帮你解决JAVA中的引用类型,强引用软引用弱引用虚拟引用所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部