欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

慢慢学JVM之强引用?软引用?弱引用?虚引用?

程序员文章站 2024-03-17 19:47:28
...

        从JDK1.2版本开始,引用被划分为4种级别,使程序能更加灵活地控制对象的生命周期。4种级别由强到弱依次为:强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)。

强引用

        强引用最普遍,正常创建一个对象就是强引用。如果一个对象的引用是强引用,那么垃圾回收器绝不会回收它,注意绝对不会回收,即使会发生OutOfMemoryError。

Object obj = new Object();

        后续软引用、弱引用、虚引用的创建都需要有一个前提,就是存在一个强引用的对象。

软引用

        软引用比强引用弱一点,当内存空间充足的时候,它不会被回收,当内存空间不足的时候,它才会被回收。软引用通过SoftReference类实现的。根据软引用的特性,软引用适合作为缓存数据的引用级别。

关于软引用回收时机的解析:https://www.jianshu.com/p/e46158238a77

         示例代码

/**
 * 运行参数: -Xmx10m -XX:+PrintGC
 */
public class Test {

    public static void main(String[] args) throws InterruptedException {
        BClass a = new BClass();
        a.setCString("a String");
        SoftReference<BClass> stringSoftReference = new SoftReference<>(a);
        System.out.println("初始化阶段-String:" + a);
        System.out.println("初始化阶段-SoftReference:" + stringSoftReference.get());

        // 去除强引用
        a = null;
        System.out.println("去除强引用阶段-SoftReference:" + stringSoftReference.get());

        // 执行FullGC
        System.gc();
        System.out.println("执行FullGC阶段-SoftReference:" + stringSoftReference.get());

        //缓存数据
        byte[] cacheData = new byte[7 * 942 * 1024]; // 6594 * 1024
        System.gc();
        System.out.println("缓存数据阶段-SoftReference:" + stringSoftReference.get());
    }
}

        结果-如果删除a = null,便不会被回收,因为对象还在被强引用着。

初始化阶段-String:BClass(cString=a String)
初始化阶段-SoftReference:BClass(cString=a String)
去除强引用阶段-SoftReference:BClass(cString=a String)
[GC (System.gc())  870K->664K(9728K), 0.0017426 secs]
[Full GC (System.gc())  664K->581K(9728K), 0.0089447 secs]
执行FullGC阶段-SoftReference:BClass(cString=a String)
[GC (Allocation Failure)  622K->613K(9728K), 0.0020401 secs]
[GC (Allocation Failure)  613K->613K(9728K), 0.0009009 secs]
[Full GC (Allocation Failure)  613K->580K(8192K), 0.0078276 secs]
[GC (Allocation Failure)  580K->580K(9728K), 0.0005522 secs]
[Full GC (Allocation Failure)  580K->568K(9728K), 0.0103109 secs]
[GC (System.gc())  7162K->7162K(9728K), 0.0009360 secs]
[Full GC (System.gc())  7162K->7162K(9728K), 0.0043472 secs]
缓存数据阶段-SoftReference:null

         这里有个小细节。当我们需要对字符串做软引用的时候,如果我们使用

String a = "a String";

并不会被回收,而是需要使用new String才能被回收。

String a = new String("a String");

弱引用

        弱引用比软引用弱一点,遇到GC就会被回收。弱引用像一个临时工,适用于那种临时工数据,在公司有难时,需要第一个做出牺牲的那种。

        示例代码

/**
 * 运行参数: -Xmx10m -XX:+PrintGC
 */
public class Test {
    public static void main(String[] args) {
        String a = new String("a String");
        WeakReference<String> weakReference = new WeakReference<>(a);

        System.out.println("初始化阶段-String:" + a);
        System.out.println("初始化阶段-WeakReference:" + weakReference.get());

        // 去除强引用
        a = null;
        System.out.println("去除强引用阶段-WeakReference:" + weakReference.get());

        // 执行FullGC
        System.gc();
        System.out.println("执行FullGC阶段-WeakReference:" + weakReference.get());
    }
}

        结果

初始化阶段-String:a String
初始化阶段-WeakReference:a String
去除强引用阶段-WeakReference:a String
[GC (System.gc())  870K->668K(9728K), 0.0010758 secs]
[Full GC (System.gc())  668K->580K(9728K), 0.0062521 secs]
执行FullGC阶段-WeakReference:null

虚引用

        虚引用跟软引用和弱引用不同,它不会影响对象的生命周期。如果一个对象仅被虚引用,相当于没有任何引用,随时可以被回收。虚引用必须要与引用队列(ReferenceQueue)一起使用。

        虚引用一般用来管理堆外内存。

        示例代码

public class Test {
    public static void main(String[] args) {
        BClass a = new BClass();
        a.setCString("a String");
        ReferenceQueue<BClass> referenceQueue = new ReferenceQueue<>();
        PhantomReference<BClass> phantomReference = new PhantomReference<>(a, referenceQueue);

        System.out.println("初始化阶段-String:" + a);
        System.out.println("初始化阶段-PhantomReference:" + phantomReference.get());

        // 去除强引用
        a = null;
        System.out.println("去除强引用阶段-PhantomReference1:" + phantomReference.get());

        // 执行FullGC
        System.gc();
        System.out.println("执行FullGC阶段-PhantomReference:" + phantomReference.get());

        Reference<?> reference = referenceQueue.poll();
        if (reference != null) {
            System.out.println("被回收对象:" + reference.get());
        }
    }
}

         结果

初始化阶段-String:BClass(cString=a String)
初始化阶段-PhantomReference:null
去除强引用阶段-PhantomReference1:null
[GC (System.gc())  2600K->768K(249344K), 0.0011438 secs]
[Full GC (System.gc())  768K->581K(249344K), 0.0056454 secs]
执行FullGC阶段-PhantomReference:null
被回收对象:null

引用队列

        引用队列可以配合软引用、弱引用和虚引用使用。引用队列的的作用是收集被JVM回收的引用对象。

        示例代码

public class Test {
    public static void main(String[] args) {
        // 创建引用队列
        ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();

        // 软引用
        SoftReference<String> softReference = new SoftReference<>(new String("softReferenceString"), referenceQueue);

        // 弱引用
        WeakReference<String> weakReference = new WeakReference<>(new String("weekReferenceString"), referenceQueue);

        // 虚引用
        PhantomReference<String> phantomReference = new PhantomReference<>(new String("phantomReferenceString"), referenceQueue);

        System.out.println("GC前SoftReference:" + softReference.get());
        System.out.println("GC前WeakReference:" + weakReference.get());
        System.out.println("GC前PhantomReference:" + phantomReference.get());

        System.gc();

        System.out.println("GC后SoftReference:" + softReference.get());
        System.out.println("GC后WeakReference:" + weakReference.get());
        System.out.println("GC后PhantomReference:" + phantomReference.get());

        Reference<?> reference = referenceQueue.poll();

        while (reference != null) {
            System.out.println("被回收引用:" + reference.get());
            reference = referenceQueue.poll();
        }
    }
}

         结果

GC前SoftReference:softReferenceString
GC前WeakReference:weekReferenceString
GC前PhantomReference:null
[GC (System.gc())  2600K->736K(249344K), 0.0011499 secs]
[Full GC (System.gc())  736K->581K(249344K), 0.0048663 secs]
GC后SoftReference:softReferenceString
GC后WeakReference:null
GC后PhantomReference:null
被回收引用:null
被回收引用:null

        当引用关联对象实现了finalize方法的话,引用队列是不起作用的。

相关标签: 抓心挠肝JVM jvm