Java基础-理解强软弱虚四种引用

107 阅读3分钟

WX20231212-102916.png

前言

本文会通过具体的代码来演示下java中的强软弱虚四种引用的基本特性。

强引用(StrongReference)

强引用, 用的最多的引用, 有强引用指向的对象,不会被gc回收, 可能会引起内存溢出。

//设置内存大小, 下面这段代码会内存溢出
List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    //不断生成大对象
    byte[] bytes = new byte[5 * 1024 * 1024];
    //添加强引用
    list.add(bytes);
}

软引用(SoftReference)

软引用一般用作内存缓存,当内存足够时,一直存在,直到内存不够用了,要抛outOfMemory异常了, 才会清理软引用。

List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    //不断生成大对象
    byte[] bytes = new byte[5 * 1024 * 1024];
    SoftReference<byte[]> softReference = new SoftReference<>(bytes);
    //添加强引用
    list.add(softReference);
}
int count = 0;
for (int i = 0; i < list.size(); i++) {
    byte[] object = list.get(i).get();
    //检测是否被回收
    if (object != null) {
        count++;
    }
}
//这里的count会比1000小, 有可能是两三百多
System.out.println(count);

弱引用(WeakReference)

Java的ThreadLocal就用的弱引用, 弱引用一般是作为一个Map的key, 当key被gc时, map的key就会为null(reference#get返回null), 然后需要编码者自己去删除无效的key-value,防止内存溢出。

List<Entry> entryList = new ArrayList<>();
List<Object> objectList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    //注意这里的key, 没有被任何地方强引用
    Object key = new Object();

    //objectList.add(key);//加上这一句就会导致内存溢出
    entryList.add(new Entry(key, new byte[5 * 1024 * 1024]));
    //这里是为了方便测试, 才会每次都检查, 实际上可以在put的时候检查
    entryList.removeIf(entry -> entry.get() == null);
}
//43
System.out.println(entryList.size());
class Entry extends WeakReference<Object> {
    private byte[] value;

    public Entry(Object key, byte[] value) {
        super(key);
        this.value = value;
    }

    public byte[] getValue() {
        return value;
    }

    public void setValue(byte[] value) {
        this.value = value;
    }
}

虚引用(PhantomReference)

虚引用用的比较少, 它需要传入一个queue, 当虚引用指向的对象进行gc时, 会往queue里面添加一个reference, 用于告诉使用者,这个对象已经被gc回收了, 可以做更深层次的清理

ReferenceQueue<Object> queue = new ReferenceQueue<>();
Object object = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<>(object, queue);
System.out.println("before gc, reference#get: " + phantomReference.get());
System.out.println("before gc, queue#poll: " + queue.poll());

// 置空强引用,让对象成为虚引用的唯一引用
object = null;

//执行垃圾回收
System.gc();

Reference<?> reference = queue.poll();
System.out.println("after gc, queue#poll: " + reference);
if (reference != null) {
  // reference.get() 一直返回null
  System.out.println("after gc, reference#get: " + reference.get());
}
before gc, reference#get: null
before gc, queue#poll: null
after gc, queue#poll: java.lang.ref.PhantomReference@5fdef03a
after gc, referecne#get: null

总结

  • 强引用 最常见的引用, 保护对象不会被GC掉。
  • 软引用 和强引用类似,但是如果要发生outOfMemory异常了, 会被清理掉。
  • 弱引用 需要搭配map(或entry)使用, 作为key,当不再需要了,就会被回收,需要代码逻辑协助清理掉value, ThreadLocal就是一个典型的例子
  • 虚引用 一般用来告知对象被已经被垃圾回收了, 可以去做进一步深度清理,比如堆外内存, 日常编码中不常见。

参考文章

medium.com/@ramtop/wea…