Bootstrap

强引用,软引用,弱应用,虚引用

学习宋红康老师和深入理解java虚拟机中关于引用的理解和笔记,如下是自己的学习整理和理解,如果有理解错误望指正


在JDK 1.2版之后,Java对引用的概念进行了扩充,将引用分为强引用(Strongly Re-ference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,这4种引用强 度依次逐渐减弱。

1.强应用

强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。 **对于强引用,jvm宁可抛出OOM也不会回收具有强应用的对象;**关于强引用就不写案例代码说明了,导致OOM的必定是在强应用,主要加深自己对软引用,弱应用,虚引用的理解;

2.软引用

软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存, 才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。大致分析就是软引用在抛出OOM异常前就回收了,所以软引用不是导致OOM的因素

​ 通过如下代码测试,设置启动参数:-Xms10m -Xmx10m -Xmn5m -XX:+PrintGCDetails

import java.lang.ref.SoftReference;

/**
 *@ClassName TestSoftReference
 *@Description 测试软引用代码
 * 软引用只有在出现内存不足的情况下回收软引用
 **/
public class TestSoftReference {

    public int i = 10;

    public static TestSoftReference testStrongReference = null;

    public static void main(String[] args) {
        TestSoftReference testSoftReference = new TestSoftReference();
        SoftReference<TestSoftReference> softReference = new SoftReference<>(testSoftReference);
        System.out.println(softReference.get());
        //将testSoftReference 置为null 再进行GC 再看看是否能获取到
        testSoftReference = null;
        System.gc();
        System.out.println("调用System.gc后:"+softReference.get());
        //让程序出现OOM
        byte[] byteArr = null;
        try {
            // 6750 是调试出来的,刚好可以达到 出现内存不足并且不抛出OOM的临界值
            byteArr = new byte[ 6750 * 1024];
        }  catch (Error e){
            e.printStackTrace();
        } finally {
            System.out.println("程序出现OOM后:" + softReference.get());
        }
    }

    @Override
    public String toString() {
        return "TestSoftReference{" +
                "i=" + i +
                '}';
    }
}

请添加图片描述

根据打印结果可以看出,并没有抛出OOM,并且在调用System.gc方法时,软引用也没有被回收,当创建了一个刚好能够让老年代内存撑满的时机后触发老年代的垃圾回收,回收之后可以看到老年代使用率处理99%还未发生OOM,但是软引用却打印为null,说明软引用发生在要发生内存溢出异常前,如果回收仍没有足够的内存, 才会抛出内存溢出异常。

3.弱引用

​弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2版之后提供了WeakReference类来实现弱引用。弱引用当触发GC时就会被回收
请添加图片描述

​ 弱引用存在两个构造方法,第二个构造方法中需要传递一个引用队列,意思是当弱引用被回收时,会将该referent添加到引用队列中,可以通过此机制对对象的生命周期进行监控,此次测试使用第一个构造方法实现。

​ 通过如下代码测试:

import java.lang.ref.WeakReference;

/**
 *@ClassName TestWeakReference
 *@Description 测试弱引用
 * 弱引用只要出现GC就会被回收
 **/
public class TestWeakReference {

    public int i = 10;

    public static TestWeakReference testWeakReference = null;

    public static void main(String[] args) {
        testWeakReference = new TestWeakReference();
        WeakReference<TestWeakReference> weakReference = new WeakReference<>(testWeakReference);
        testWeakReference = null;
        //为出现GC前
        System.out.println(weakReference.get());
        System.gc();
        System.out.println(weakReference.get());
    }

    @Override
    public String toString() {
        return "TestWeakReference{" +
                "i=" + i +
                '}';
    }
}

打印结果为:

TestWeakReference{i=10}
null

4.虚引用

​ 虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。**一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。**为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供了PhantomReference类来实现虚引用;**虚引用实现提供了一个构造方法,通过创建引用队列来创建虚引用,可以通过此机制对对象的生命周期进行监控。
请添加图片描述
通过如下代码测试:

package test.example.jvm;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

/**
 *@ClassName TestPhantomReference
 *@Description 测试虚引用
 * 虚引用会
 **/
public class TestPhantomReference {

    int i = 10;

    public static TestPhantomReference testPhantomReference = null;

    public static ReferenceQueue<TestPhantomReference> referenceQueue = null;

    public static class CheckThread extends Thread {
        @Override
        public void run() {
            while (true) {
                if (referenceQueue != null) {
                    PhantomReference<TestPhantomReference> remove = null;
                    try {
                        remove = (PhantomReference<TestPhantomReference>) referenceQueue.remove();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (remove != null) {
                        System.out.println("监控到类型为TestPhantomReference的对象被回收");
                    }
                }

            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        CheckThread checkThread = new CheckThread();
        checkThread.setDaemon(true);
        checkThread.start();

        testPhantomReference = new TestPhantomReference();
        referenceQueue = new ReferenceQueue<>();
        PhantomReference<TestPhantomReference> phantomReference = new PhantomReference<>(testPhantomReference, referenceQueue);
        System.out.println(phantomReference.get());
        testPhantomReference = null;
        System.gc();
        if (testPhantomReference != null) {
            System.out.println("对象还活着");
        } else {
            System.out.println("对象已回收");
        }
        Thread.sleep(1000);
    }

    @Override
    public String toString() {
        return "TestPhantomReference{" +
                "i=" + i +
                '}';
    }
}

打印结果如下:

null
监控到类型为TestPhantomReference的对象被回收
对象已回收

结果分析:虚引用甚至无法获取对象的实例,但是当引用对象被回收的时候,还是可以通过引用队列观察到对象被回收

;