JVM四种引用原理

四种引用

  1. 强引用
    只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

  2. 软引用(SoftReference)
    仅有软引用引用该对象时,在一次垃圾回收后内存仍不足时,会再次触发垃圾回收,回收软引用对象
    可以配合引用队列来释放软引用自身

  3. 弱引用(WeakReference)
    仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
    可以配合引用队列来释放弱引用自身

  4. 虚引用(PhantomReference)
    必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,
    由 Reference Handler 线程调用虚引用相关方法释放直接内存

原理概述

下面的长篇大论先不看,简短的你看明白了下面的就可以不用看了:

1、Java设置了三个类:SoftReference(软引用)、WeakReference(弱引用)、PhantomReference(虚引用)

2、如何使用这些对象?new SoftReference(目标对象【可选参数:,引用队列】)、new WeakReference(目标对象【可选参数:,引用队列】)、new PhantomReference(目标对象,引用队列)

3、目标对象,就是我们想要赋予特殊引用的对象,比如我想给一个Integer对象设置一个软引用,那就new SofeReference(Integer对象)

4、那么JVM在垃圾回收时,对SoftReference(软引用)、WeakReference(弱引用)、PhantomReference(虚引用)的属性(前提是这个属性,只剩下这个引用,没有强引用了)特殊处理,对于它们的属性不作为强引用来处理,如果还指定了引用队列的话,那么就会在回收掉他们的属性后,把它们添加到引用队列中,而我们可以创建一个线程监听引用队列,当发现引用队列新增了元素,那就代表有目标对象被回收了,我们做出相应操作即可。

引用原理

  • 强引用:默认一个对象就是强引用
  • 软引用/弱引用:创建SoftReference/WeakReference对象,然后把目标对象作为它们的属性,这样就实现了软/弱引用,但是要注意的是,此时SoftReference/WeakReference对象是强引用,而它们的属性才是软/弱引用,垃圾回收只会回收它们的属性,而它们是作为强引用对象存活下来的
    • 软/弱引用对象如果不指定引用队列,那么它们就只有一个地方有强引用,那就是声明他们俩的地方,出了这个地方,他们就失去强引用了,所以即使不需要引用队列,只要没有逃逸到方法外,软/弱引用对象就会自然失去强引用,然后顺其自然的被回收
    • 软/弱引用对象如果指定引用队列,那么它们就有两个地方有强引用,那就是声明他们俩的地方引用队列中会引用它们(JVM回收目标对象后,会把软/弱引用对象放到引用队列中),要注意去掉这两个强引用,不然浪费空间,一般情况下都会自然而然的失去强引用
// 软引用演示
Integer i = new Integer(1);		// 执行完这行代码,堆里面的new Integer(1)对象 此时具有 强引用
SoftReference<Integer> ref = new SoftReference<>(i);	// 执行完这行代码,堆里面的new Integer(1)对象 此时具有 强引用、软引用 两种引用,垃圾回收无法回收堆里面的new Integer(1)对象;如果只有SoftReference<Integer> ref = new SoftReference<>(new Integer(1));,没有前面的一行,那么此时堆里面的new Integer(1)对象只有 弱引用,没有强引用,此时发生垃圾回收,堆里面的new Integer(1)对象有几率被回收
i = null;	// 执行完这行代码,堆里面的new Integer(1)对象 此时只具有 弱引用,失去了强引用,如果此时发生垃圾回收,堆里面的new Integer(1)对象有几率被回收
Integer j = ref.get();	// 如果执行完这行代码 ,堆里面的new Integer(1)对象还没有被回收,那ref.get()返回的是就堆里面的new Integer(1)对象的引用,反之返回的就是null了;如果还没有被回收的话,那堆里面的new Integer(1)对象就重新获得了 强引用,此时具有 强引用、软引用 两种引用,此时发生垃圾回收,无法回收堆里面的new Integer(1)对象

// 软引用 + 引用队列 演示
// 要注意,上面的案例,SoftReference<Integer> ref,这个对象,他是强引用的,直到Integer i会被回收了,它也是是没有被回收的,那这样不就是等于是浪费了一个对象的内存空间么,没错,所以可以配合 引用对象来把这个对象也回收掉
// 引用队列
ReferenceQueue<Integer> queue = new ReferenceQueue<>();

// 关联了引用队列,当软引用所关联的 byte[] 被回收时,软引用自己会加入到 queue 中去
SoftReference<Integer> ref = new SoftReference<>(new Integer(1), queue);
System.out.println(ref.get());

// 从队列中获取无用的 软引用对象,并移除
Reference<? extends byte[]> poll = queue.poll();
while(poll != null) {

    list.remove(poll);
    poll = queue.poll();
}
  • 虚引用:虚引用和必须配合引用队列使用,他跟对象回收的条件没有任何关系,他只是会在一个对象被回收以后(对象被回收后,虚引用对象会被放到指定的引用队列中),被放到在构造方法中指定好的引用队列中,看虚引用的构造方法就可以发现,虚引用只有一个必须指定引用队列的构造方法public PhantomReference(T referent, ReferenceQueue<? super T> q),而软/弱引用有两个构造方法(目标对象)(目标对象,引用队列)
// 虚引用使用演示
public static void main(String[] args) {
    Integer i = new Integer(1);
    ReferenceQueue<Integer> queue = new ReferenceQueue<>();
    PhantomReference<Integer> reference = new PhantomReference<>(i,queue);

    System.out.println("i = " + i);
    System.out.println("queue = " + queue);
    System.out.println("reference = " + reference);

    System.out.println("i = null...");
    i = null;
    System.out.println("gc...");
    System.gc();

    System.out.println("i = " + i);
    System.out.println("queue = " + queue);
    System.out.println("reference = " + reference);

    // 这里监听线程故意写在后面,为了执行顺序看的清晰一点,其实写在哪里都是可以的
    Thread thread = new Thread(() -> {
        while (true) {
            Reference<? extends Integer> poll = queue.poll();
            if (poll != null) {
                System.out.println("变量i被回收了!");
                System.out.println("当前的 queue =" + queue);
                System.out.println("当前的 reference =" + reference);
                System.out.println("传递给我的是" + poll);
            }
        }
    });
    thread.setDaemon(true);
    thread.start();
}

虚引用和对象回收条件没有任何关系

  • 再次强调!!!虚引用和对象回收条件没有任何关系,你可以理解为虚引用,就是没有引用,那么这个对象就会被GC回收掉,但是被回收掉以后,虚引用对象(把被回收对象作为属性的虚引用对象)会被放到引用队列中,然后会有线程去做对应的操作(这个线程得我们去控制,不然我们不使用线程针对引用队列进行操作,那么对象回收以后,也不会有任何操作,这个操作是我们去做的,JVM只负责把虚引用对象放到引用队列,算是通知我们,这个虚引用对象的属性【我们的目标对象】被回收了,然后我们根据这个通知区做自定义的操作,我们收到通知,不做操作,也是可以的,但是不做操作,那虚引用的意义在哪呢哈哈哈)

虚引用在DirectByteBuffer中回收直接内存的应用

  • DirectByteBuffer使用的直接内存就是这样回收的,DirectByteBuffer同时具有强引用虚引用,当失去强引用后,下次垃圾回收的时候,就会被回收掉,同时虚引用对象就会被放到引用队列中,然后监听引用队列的线程,就会从引用队列中拿出虚引用对象,然后做释放直接内存的操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值