你知道Java四种引用吗?以及他们是如何回收的?

2020-12-24  本文已影响0人  小驴小驴

Java四种引用

引用队列

引用队列 】用于将软引用和弱引用、虚引用的引用者放入该队列,当他们所引用的对象被垃圾回收之后,Reference引用关系就会进入该队列等待被线程回收。其中软引用和弱引用可以不配合引用队列使用,但是虚引用一定需要配合该队列。

实例程序

前提:为了测出效果,这里把堆内存的大小修改为20M,在启动参数中添加-Xmx20m;

- 强引用
public static void main(String[] args) {
    List<byte[]> list = new ArrayList<>();    
    for (int i = 0; i < 4; i++) {
        list.add(new byte[1024 * 1024 * 5]);    
    }
}
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

说明:
这种是强引用的案例,我们日常工作中大多数对象都是被强引用。即GC Roots所能直接或间接的引用到的对象。这部分在GC时不会被回收,因为这些byte[]始终被生命周期更长的list引用着。因此当byte[]太多之后势必造成堆OOM。

- 软引用
public static void soft() {
    List<SoftReference<byte[]>> lists = new ArrayList<>();    
    for (int i = 0; i < 5; i++) {
        lists.add(new SoftReference<>(new byte[1024 * 1024 * 4]));
        System.out.println("==============================");
        for (SoftReference<byte[]> list : lists) {
             System.out.println(list.get()); 
        }
    }
}
运行结果:
==============================
[B@682a0b20
==============================
[B@682a0b20
[B@3d075dc0
==============================
[B@682a0b20
[B@3d075dc0
[B@214c265e
==============================
[B@682a0b20
[B@3d075dc0
[B@214c265e
[B@448139f0
==============================
null
null
null
null
[B@7cca494b

说明:软引用在发生GC时,且堆内存空间不足时,就会释放被软引用所引用的对象。

- 弱引用
public static void weak() {
    List<WeakReference<byte[]>> lists = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        lists.add(new WeakReference<>(new byte[1024*1024*4]));
        System.out.println("========================");
        for (WeakReference<byte[]> list : lists) {
            System.out.println(list.get());
        }
   }
}
运行结果:
========================
[B@682a0b20
========================
[B@682a0b20
[B@3d075dc0
========================
[B@682a0b20
[B@3d075dc0
[B@214c265e
========================
null
null
null
[B@448139f0
========================
null
null
null
[B@448139f0
[B@7cca494b

说明:软引用在GC时,不管内存是否够用都会删除只被软引用所引用的对象。

- 虚引用
public static void fake() throws IOException {
    Runnable runnable = () -> System.out.println("虚引用");
    Object obj = new Object();
    Cleaner.create(obj, runnable);
    System.in.read();
    obj = null;
    System.gc();
    System.in.read();
}
运行结果:
虚引用

说明:如上是一个虚引用的小Demo,也是ByteBuffer开辟直接内存的一个回收缩影。上面的核心类为Cleaner类,该类继承PhantomReference类,所以其本质就是一个虚引用。上述的create方法就是使用虚引用来引用obj对象,当obj对象被垃圾释放时,引用关系会被放入到ReferenceQueue队列中,之后会有一个线程ReferenceHandler线程一直从Queue中取出Reference对象并调用clean方法,而clean方法就会调用create传入的第二个线程参数。
源码如下:

回顾引用队列

在了解了上述背景之后,你可能想问。在软、弱引用。在被引用的对象在相应的场景被释放之后。那么这一层Reference引用关系是如何被清理的呢?仔细看一下下一段源码:


1497_0.png

可以看到,ReferenceHandler线程会把垃圾回收器带来的Reference对象置空以下一次被垃圾回收(这个discovered就是Reference实例)。

上一篇 下一篇

猜你喜欢

热点阅读