Java 中的强引用、软引用、弱引用、虚引用、引用队列

Java GC 面试 大约 7526 字

强引用

不会被垃圾回收,OOM也不会对该对象进行回收。因此强引用也是造成Java内存泄漏的主要原因之一。

new出来的对象为强引用,obj2指向obj1obj2也就拥有了Object对象的内存地址,obj2也是强引用。

public static void main(String[] args) {
    Object obj1 = new Object();
    Object obj2 = obj1;
    obj1 = null;
    System.gc();
    System.out.println("obj1=" + obj1);
    System.out.println("obj2=" + obj2);
}

可以看到垃圾回收不会回收强引用。

obj1=null
obj2=java.lang.Object@1540e19d

软引用

内存足够的情况的下,不会回收,内存不够的情况下,会回收。

软引用通常用在内存敏感的程序中,如高速缓存就用到了软引用,MyBatis也有用到软引用做缓存。

内存足够

public static void memoryEnough() {
    Object obj1 = new Object();
    SoftReference<Object> softReference = new SoftReference<>(obj1);
    System.out.println("obj1=" + obj1);
    System.out.println("obj1 in soft reference=" + softReference.get());
    System.out.println("soft reference=" + softReference);

    obj1 = null;
    System.gc();

    System.out.println("------------ Memory Enough After GC ------------");

    System.out.println("obj1=" + obj1);
    System.out.println("obj1 in soft reference=" + softReference.get());
    System.out.println("soft reference=" + softReference);

}
obj1=java.lang.Object@1540e19d
obj1 in soft reference=java.lang.Object@1540e19d
soft reference=java.lang.ref.SoftReference@677327b6
------------ Memory Enough After GC ------------
obj1=null
obj1 in soft reference=java.lang.Object@1540e19d
soft reference=java.lang.ref.SoftReference@677327b6

内存不足

启动时添加-Xmx5m限制最大内存,再分配一个30M的字节数组人为制造OOM

// -Xms5m -Xmx5m
public static void memoryNotEnough() {
    Object obj1 = new Object();
    SoftReference<Object> softReference = new SoftReference<>(obj1);
    System.out.println("obj1=" + obj1);
    System.out.println("obj1 in soft reference=" + softReference.get());
    System.out.println("soft reference=" + softReference);

    obj1 = null;

    System.out.println("------------ Memory Not Enough OOM ------------");

    try {
        byte[] bytes = new byte[30 * 1024 * 1024]; // 30MB 内存
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        System.out.println("obj1=" + obj1);
        System.out.println("obj1 in soft reference=" + softReference.get());
        System.out.println("soft reference=" + softReference);
    }
}

可以看到obj1在软引用中的对象被回收了。

注意:softReference自身是new出来的,所以softReference是强引用,不会被回收。回收的是softReference中包裹的对象obj1

obj1=java.lang.Object@1540e19d
obj1 in soft reference=java.lang.Object@1540e19d
soft reference=java.lang.ref.SoftReference@677327b6
------------ Memory Not Enough OOM ------------
obj1=null
obj1 in soft reference=null
soft reference=java.lang.ref.SoftReference@677327b6
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at SoftReferenceDemo.memoryNotEnough(SoftReferenceDemo.java:36)
    at SoftReferenceDemo.main(SoftReferenceDemo.java:48)

弱引用

只要垃圾回收机制一运行,不管JVM内存是否足够,都会回收弱引用中的对象。

public static void main(String[] args) {
    Object obj1 = new Object();
    WeakReference<Object> weakReference = new WeakReference<>(obj1);
    System.out.println("obj1=" + obj1);
    System.out.println("obj1 in weak reference=" + weakReference.get());
    System.out.println("weak reference=" + weakReference);


    obj1 = null;
    System.gc();

    System.out.println("------------ After GC ------------");

    System.out.println("obj1=" + obj1);
    System.out.println("obj1 in weak reference=" + weakReference.get());
    System.out.println("weak reference=" + weakReference);
}

同样:weakReference自身是new出来的,所以weakReference是强引用,不会被回收。回收的是weakReference中包裹的对象obj1

obj1=java.lang.Object@1540e19d
obj1 in weak reference=java.lang.Object@1540e19d
weak reference=java.lang.ref.WeakReference@677327b6
------------ After GC ------------
obj1=null
obj1 in weak reference=null
weak reference=java.lang.ref.WeakReference@677327b6

注意:因为运行中的线程是GCRoot,所以及时置为null了,也不会被回收。具体表现为,虚引用一直持有运行中的Thread的引用,无论是否GC

Thread thread1 = new Thread(() -> {
    System.out.println("111111111");
    while (true) {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("22222222222");
    }
});
thread1.start();

WeakReference<Thread> wr = new WeakReference<>(thread1);

thread1 = null;
System.gc();

System.out.println("------------ After GC ------------");

System.out.println("weak reference=" + wr.get());

输出:

------------ After GC ------------
111111111
weak reference=Thread[Thread-0,5,main]
22222222222
22222222222

虚引用

如同和没有任何引用一样,任何时候都可能被垃圾回收,虚引用不能单独使用也不能通过它访问对象,虚引用必须和引用队列ReferenceQueue联合使用。

虚引用主要作用是跟踪对象被垃圾回收的状态,仅仅提供了一种确保对象被finalize后,做某些事的机制。

PhantomReferenceget方法总是返回null

虚引用被回收后进入引用队列,从引用队列中poll出来的Reference对象可以通过反射获取其内部保存的私有成员变量referent,再通过get方法获取保存在虚引用中的对象。

public static void main(String[] args) throws InterruptedException {
    Object obj1 = new Object();
    ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
    PhantomReference<Object> phantomReference = new PhantomReference<>(obj1, referenceQueue);

    System.out.println("obj1=" + obj1);
    System.out.println("obj1 in phantom reference=" + phantomReference.get());
    System.out.println(referenceQueue.poll());
    System.out.println("phantom reference=" + phantomReference);

    System.out.println("-----------------------------------");

    obj1 = null;
    System.gc();
    Thread.sleep(500);

    System.out.println("obj1=" + obj1);
    System.out.println("obj1 in phantom reference=" + phantomReference.get());
    Reference<?> pollReference = referenceQueue.poll();
    System.out.println(pollReference);
    System.out.println("phantom reference=" + phantomReference);

    if (pollReference != null) {
        try {
            Field rereferent = Reference.class.getDeclaredField("referent");
            rereferent.setAccessible(true);
            Object result = rereferent.get(pollReference);
            System.out.println("gc will collect#" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出:

obj1=java.lang.Object@1540e19d
obj1 in phantom reference=null
null
phantom reference=java.lang.ref.PhantomReference@677327b6
-----------------------------------
obj1=null
obj1 in phantom reference=null
java.lang.ref.PhantomReference@677327b6
phantom reference=java.lang.ref.PhantomReference@677327b6
gc will collect#java.lang.Object@1540e19d

引用队列

软引用、弱引用、虚引用可以在构造时传入引用队列,当引用中的对象被回收时会进入引用队列。

public static void main(String[] args) throws InterruptedException {
    Object obj1 = new Object();
    ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
    WeakReference<Object> weakReference = new WeakReference<>(obj1, referenceQueue);

    System.out.println("obj1=" + obj1);
    System.out.println("obj1 in weak reference=" + weakReference.get());
    System.out.println(referenceQueue.poll());
    System.out.println("weak reference=" + weakReference);

    System.out.println("-----------------------------------");

    obj1 = null;
    System.gc();
    Thread.sleep(500);

    System.out.println("obj1=" + obj1);
    System.out.println("obj1 in weak reference=" + weakReference.get());
    System.out.println(referenceQueue.poll());
    System.out.println("weak reference=" + weakReference);
}

输出:

obj1=java.lang.Object@1540e19d
obj1 in weak reference=java.lang.Object@1540e19d
null
weak reference=java.lang.ref.WeakReference@677327b6
-----------------------------------
obj1=null
obj1 in weak reference=null
java.lang.ref.WeakReference@677327b6
weak reference=java.lang.ref.WeakReference@677327b6
阅读 475 · 发布于 2021-04-02

————        END        ————

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批