自学内容网 自学内容网

Java强软弱虚四大引用


提示:以下是本篇文章正文内容,下面案例可供参考

一、强引用

在Java中,强引用(Strong Reference)是最常见的引用类型。当我们创建一个对象并将其赋值给一个变量时,实际上就是创建了一个强引用。这意味着只要这个引用变量还存在,垃圾回收器就不会回收它所引用的对象。(除非等于null)

简单来说日常使用的大部分都是强引用,即使OOM也不回收,所以往往是导致内存泄露的元凶

代码如下(示例):

public class Test {
    public static void main(String[] args) throws Throwable {
        /**
         * 虚拟机配置选项
         * VM Options :-XX:+PrintGCDetails -Xms10m -Xmx10m
         * -XX:+PrintGCDetails:打印 GC 的详细信息
         * -Xms10m:设置初始化堆空间大小为 10m
         * -Xmx10m:设置最大堆空间大小为 10m
         */
        // 强引用
        byte[] bytes = new byte[4 * 1024 * 1024];
        // 调用 GC
        System.gc();
}
}

输出结果(如下):

可以看出 bytes 并没有存储任何数据,仅仅是开辟空间,但是 GC 后仍然存在

在这里插入图片描述

在这里插入图片描述


二、软引用

在Java中,软引用(SoftReference)是一种特殊类型的引用,它比强引用弱,但比弱引用强。当系统内存不足时,即 JVM 空间即将耗尽,垃圾回收器会优先清理那些只被软引用关联的对象。如果一个对象只剩下软引用,那么在内存充足的情况下,该对象不会被回收;但在内存不足时,这个软引用指向的对象会被垃圾回收器回收以释放内存。
软引用通常用于实现内存敏感的缓存,例如图片缓存。当系统内存紧张时,这些缓存可以被自动清除,从而避免了 OutOfMemoryError 异常的发生,同时,在内存资源相对充足时,又能保持缓存的功能,提高程序性能。

简单来说,就是内存不足是才回收

代码如下(示例):

public class Test {
    public static void main(String[] args) throws Throwable {
        /**
         * 虚拟机配置选项
         * VM Options :-XX:+PrintGCDetails -Xms10m -Xmx10m
         * -XX:+PrintGCDetails:打印 GC 的详细信息
         * -Xms10m:设置初始化堆空间大小为 10m
         * -Xmx10m:设置最大堆空间大小为 10m
         */
        // 软引用
        Map<String, SoftReference<byte[]>> map = new HashMap<>();
        // 最大 10m 的堆空间肯定装不 24m 的数组,所以 JVM 会自动 GC 回收
        map.put("bytes1", new SoftReference<>(new byte[6 * 1024 * 1024]));
        map.put("bytes2", new SoftReference<>(new byte[6 * 1024 * 1024]));
        map.put("bytes3", new SoftReference<>(new byte[6 * 1024 * 1024]));
        // 强引用数组,无法回收
        byte[] bytes2 = new byte[6 * 1024 * 1024];
}
}

输出结果(如下):

可以看出JVM多次调用 GC 回收软引用
在这里插入图片描述


三、弱引用

在Java中,弱引用(WeakReference)是一种特殊的引用类型,它代表的对象可被垃圾回收器在任何时候回收,即使该对象仍然有弱引用存在。相比于强引用(普通引用),只要强引用还在,垃圾回收器就不会回收该对象;而软引用和弱引用都允许在内存不足时回收其引用的对象,但软引用会在系统内存尚充足的情况下尽可能保留对象,而弱引用则不考虑当前内存状况。
弱引用的典型使用场景是在缓存或者映射关系中,当主要持有对象的强引用消失后,即使还有其他地方通过弱引用关联到这个对象,也依然会被垃圾回收器视为无用对象并进行回收。

简单来说,就是只要GC就会回收

代码如下(示例):

public class Test {
    public static void main(String[] args) {
        /**
         * 虚拟机配置选项
         * VM Options :-XX:+PrintGCDetails
         * -XX:+PrintGCDetails:打印 GC 的详细信息
         */
        // 弱引用
        // 开辟一个很小的数组
        WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[10]);
        // GC 前
        System.out.printf(Arrays.toString(weakReference.get()));
        System.out.println();
        System.gc();
        // GC 后
        System.out.printf(Arrays.toString(weakReference.get()));
        System.out.println();
    }
}

输出结果(如下):

可以看出这个很小的数组在GC后直接没了,即使空间充足

在这里插入图片描述


四、虚引用

虚引用,顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象,虚引用必须和引用队列(ReferenceQueue)联合使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。
PhantomReference的get方法总是返回null,因此无法访问对应的引用对象。其意义在于说明一个对象已经进入finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作。换句话说,设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

源代码如下:

public class PhantomReference<T> extends Reference<T> {

    /**
     * Returns this reference object's referent.  Because the referent of a
     * phantom reference is always inaccessible, this method always returns
     * <code>null</code>.
     *
     * @return  <code>null</code>
     */
    public T get() {
        return null;
    }

    /**
     * Creates a new phantom reference that refers to the given object and
     * is registered with the given queue.
     *
     * <p> It is possible to create a phantom reference with a <tt>null</tt>
     * queue, but such a reference is completely useless: Its <tt>get</tt>
     * method will always return null and, since it does not have a queue, it
     * will never be enqueued.
     *
     * @param referent the object the new phantom reference will refer to
     * @param q the queue with which the reference is to be registered,
     *          or <tt>null</tt> if registration is not required
     */
    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}

简单来说就是虚引用不存储任何东西,且必须配合引用队列使用,它唯一的作用就是监控绑定的对象是否被回收

代码如下(示例):


public class Test {
    public static void main(String[] args) {
        // 虚引用
        // 作用:监控对象的回收情况

        // 要监控的对象
        Object o1 = new Object();
        // 引用队列
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        // 虚引用绑定对象和引用队列
        PhantomReference<Object> phantomReference = new PhantomReference<>(o1, referenceQueue);

        // 回收前的被监控的对象
        System.out.println("回收前的被监控的对象: " + o1);
        // 虚引用地址
        System.out.println("虚引用地址: " + phantomReference);
        // 源码可知,总是返回 null
        System.out.println("虚引用的返回值: " + phantomReference.get());
        // 所以队列也返回 null
        System.out.println("引用队列的返回值: " + referenceQueue.poll());

        System.out.println("====================");
        // 置空监控的对象
        o1 = null;
        // 回收对象
        System.gc();
        // 监控的对象
        System.out.println("回收后的对象: " +o1);
        // 虚引用地址
        System.out.println( "虚引用地址: " + phantomReference);
        // 源码可知,总是返回 null
        System.out.println("虚引用的返回值: " + phantomReference.get());
        // 重点!!!!!
        // 引用队列中存储了虚引用,说明该对象被回收了
        // 这就是虚引用的唯一目的,即 gc 后将自身加入到引用队列
        System.out.println("引用队列的返回值: " + referenceQueue.poll());
    }
}

输出结果(如下):

在这里插入图片描述



原文地址:https://blog.csdn.net/m0_69269392/article/details/135722730

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!