基础 | java - [强引用、软引用、弱引用、虚引用]

§1 Reference

在 JDK 中,引用和对象一样,已经被抽象成了类,如下图
在这里插入图片描述

§2 各引用级别

强引用 FinalReference

  • 强引用是最常见的引用,但 FinalReference 是非 public 的,因此不能如其他引用一样显示声明
  • 对象赋值给一个引用型变量,此变量就是一个强引用
  • 只要对象持有任意强引用,即可免疫 GC
    但可能造成无用对象依然被强引用指向,所以是内存泄漏的主因
    通常,对象超出引用作用于或显式置空(= null)就认为可以作为垃圾回收(还要参考垃圾回收策略)

强引用 Finalizer

  • Finalizer 是对 FinalReference 的实现,也是非 public 的

  • 实现有 protected void finalize() throws Throwable { } 方法的对象会被识别为 Finalizer 对象

  • Finalizer 对象由 JVM 自动调用 register() 方法,完成创建,并在创建时将自己关联引用队列

  • Finalizer 通过 finalize() 定义了对象销毁时的自定义动作

  • Finalizer 对象至少需要两轮 GC 才能被回收,这是为了使被忘记释放资源的对象,即使真被忘了,也不会肯定不被回收

    • 第一轮:通过 finalize() 将 Finalizer 对象的连接放到它的引用队列中,但因为 Finalizer 本身就是个强引用所以还没有被 GC
    • 第二轮:若 Finalizer 对象只持有 Finalizer 引用队列的强引用了,才会通过正常方式 GC

软引用 SoftReference

  • 软引用可以通过下面方式声明 Reference<AAA> ra = new SoftReference<>(new AAA());
  • 支持有软引用的对象在 JVM 内存充足时免疫 GC,但不充足时会被回收,如下例
  • 内存不足 的界定
    • 快要 OOM 前,JVM 触发 GC
    • 执行后若依然要 OOM,认定内存不足,触发二次 GC,此时软引用纳入回收范围(下图中,可见两次 Full GC)
    • 若执行后依然要 OOM,那就 OOM 了
  • 可用于内存敏感型缓存 Map<String,SoftReference<CacheDdata>> cache = new HashMap<>();
public class SoftReferenceDemo {
    public AAA b = new AAA();
    private Reference<AAA> ra = new SoftReference<>(new AAA());

    public Reference<AAA> getRa() {
        return ra;
    }

    public static void main(String[] args) {
        SoftReferenceDemo demo = new SoftReferenceDemo();
        System.out.println("================================");
        System.out.println(demo.b);
        System.out.println(demo.getRa().get());
        System.out.println("================================");
        try {
            int[] boom = new int[30*1024*1024];
        }catch (Throwable e){
            System.out.println("********************************");
            System.out.println("boom !!!");
            System.out.println("********************************");
        }finally {
            System.out.println("================================");
            System.out.println(demo.b);
            System.out.println(demo.getRa().get());
            System.out.println("================================");
        }
    }
}

在这里插入图片描述
弱引用 WeakReference

  • 弱引用可以通过下面方式声明 Reference<AAA> ra = new WeakReference<>(new AAA());
  • 只要 GC 就会被回收
  • 可以用来解决内存泄漏 Map<String,WeakReference> map = new HashMap<>();WeakHashMap
    这种方式可以省略从容器中移除不需要的 key 的操作,以保证容器中的数据尽量齐全还不会导致内存泄漏
    但注意 线程不安全,同时只能用于存储 相对无关紧要 的数据,否则一个 GC 就丢了
public class WeakReferenceDemo {
    public AAA b = new AAA();
    private Reference<AAA> ra = new WeakReference<>(new AAA());

    public Reference<AAA> getRa() {
        return ra;
    }

    public static void main(String[] args) {
        WeakReferenceDemo demo = new WeakReferenceDemo();
        System.out.println("================================");
        System.out.println(demo.b);
        System.out.println(demo.getRa().get());
        System.out.println("================================");

        System.gc();

        System.out.println("================================");
        System.out.println(demo.b);
        System.out.println(demo.getRa().get());
        System.out.println("================================");
    }
}

在这里插入图片描述

WeakHashMap
WeakHashMap 是个 弱引用 版的 HashMap,其内部的 WeakHashMap.Entry 中 key 的类型为 WeakReference<Object>
这意味着,一旦 GC,只要 key 不被其他强引用指向(比如是在字符串,在常量池里),WeakHashMap 就地清空
见下面源码片段,和测试示例

	// 此 Entry 非彼 Entry
	Entry<K,V>[] table;
    public WeakHashMap(int initialCapacity, float loadFactor) {
        // 省略
        table = newTable(capacity);
        // 省略
    }
    // 此 Entry 中的 key 是 弱引用,所以一旦 GC,只要 key 不被其他强引用指向, WeakHashMap 就地清空
    private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        final int hash;
        Entry<K,V> next;
		// 构造,注意 key 才是弱引用
		Entry(Object key, V value,
              ReferenceQueue<Object> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }
        //省略
     }

简单的测试
在这里插入图片描述

虚引用 PhantomReference

  • 虚引用,也称幽灵引用,是特殊引用
    • 不能往往对象(虚引用的 get() 永远返回 null)
    • 不影响对象的声明周期(不影响对象的 GC 抗性)
    • 仅持有虚引用的对象等同于 无引用,随时可能被 GC
  • 必须结合 ReferenceQueue 一起使用
  • 用于跟踪对象被 GC 的状态,实现当对象 finalize 后触发做某些事的机制,以实现比 finalization 机制更灵活的回收操作
    对象终止(finalization)机制:GC 对象前会先调用对象的 finalize() 方法,此方法允许重写。通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等

ReferenceQueue + WeakReference
默认的,如果 SoftReference、WeakReference、PhantomReference 的对象 在声明时指定了 引用队列,如下例,则被 GC 时会执行 finalize() 方法,对象销毁后,上述引用本身不在指向对象,但 对象会存入 引用队列

public void test() throws InterruptedException {
    ReferenceQueue<AAA> rf = new ReferenceQueue<>();
    Reference<AAA> weak = new WeakReference<>(new AAA(),rf);

    System.out.println(weak.get());//base.learning.AAA@3cd1a2f1
    System.out.println(rf.poll());//null

    System.gc();
    TimeUnit.SECONDS.sleep(1);

    System.out.println(weak.get());//null
    System.out.println(rf.poll());//引用还在 java.lang.ref.WeakReference@2f0e140b
//        System.out.println(rf.poll().get());//引用不指向对象了 jnull
}

ReferenceQueue + PhantomReference

public void test() throws InterruptedException {
    ReferenceQueue<AAA> rf = new ReferenceQueue<>();
    Reference<AAA> phantom = new PhantomReference<>(new AAA(),rf);

    System.out.println(phantom.get());
    System.out.println(rf.poll());

    System.gc();
    TimeUnit.SECONDS.sleep(1);

    System.out.println(phantom.get());
    System.out.println(rf.poll());
}

在这里插入图片描述

四大引用对比
四大引用的区别,主要集中在 对 GC 抵抗能力

对应类ReferenceSoftReferenceWeakReferencePhantomReference
是否常见×××
使用方式常规场景都是
比如正常赋值
但不能如其他引用直接声明
Reference<AAA> ra = new SoftReference<>(new AAA());类似 SoftReference配合引用队列
new PhantomReference<>(new AAA(),rf);
只持有此类引用的对象遇到垃圾回收时OOM 了都不收内存不足时回收只要 GC 就回收随时回收
应用场景遍地都是内存敏感缓存(如 mybatis 中)解决容器不移除过期 key
导致的内存泄漏
WeakHashMap
扩展 finalization 机制

ReferenceQueuefinalize() 的区别

  • finalize() 是 JVM 回收对象之前自动调用,即在回收之前生效
  • ReferenceQueue 可以在对象被回收之后,额外保存其引用,即在回收之后生效

本文部分内容摘自
Java Reference详解
JDK源码分析之FinalReference完全解读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值