Java中的各种引用(强引用、软引用、弱引用、虚引用)(五)

前言

Java中的各种引用(强引用、软引用、弱引用、虚引用)(一)
Java中的各种引用(强引用、软引用、弱引用、虚引用)(二)
Java中的各种引用(强引用、软引用、弱引用、虚引用)(三)
Java中的各种引用(强引用、软引用、弱引用、虚引用)(四)
这里先不说虚引用,先说一下引用队列ReferenceQueue

问题

问题一

第一个问题:当软引用和弱引用的引用对象因为GC或者内存不足被回收了以后,SoftReferences对象和WeakReferences对象是否还存在?
code

package com.test.application;

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

public class References {

    public static void main(String[] args) {
        byte[] bytes = new byte[1024 * 1024];
        SoftReference<byte[]> soft = new SoftReference<>(new byte[1024 * 1024 * 6]);
        WeakReference<byte[]> weak = new WeakReference<>(new byte[1024 * 1024 * 5]);
        System.out.println("bytes = " + bytes);
        System.out.println("soft = " + soft);
        System.out.println("soft.get() = " + soft.get());
        System.out.println("weak = " + weak);
        System.out.println("weak.get() = " + weak.get());
        byte[] b = new byte[1024 * 1024 * 10];
        System.out.println("bytes = " + bytes);
        System.out.println("soft = " + soft);
        System.out.println("soft.get() = " + soft.get());
        System.out.println("weak = " + weak);
        System.out.println("weak.get() = " + weak.get());
        System.out.println("b = " + b);
    }
}

result

bytes = [B@140e19d
soft = java.lang.ref.SoftReference@17327b6
soft.get() = [B@14ae5a5
weak = java.lang.ref.WeakReference@131245a
weak.get() = [B@16f6e28
[GC (Allocation Failure)  13965K->13020K(19712K), 0.0012468 secs]
[Full GC (Ergonomics)  13020K->7800K(19712K), 0.0037406 secs]
[GC (Allocation Failure)  7800K->7800K(19712K), 0.0002070 secs]
[Full GC (Allocation Failure)  7800K->1641K(19712K), 0.0040752 secs]
bytes = [B@140e19d
soft = java.lang.ref.SoftReference@17327b6
soft.get() = null
weak = java.lang.ref.WeakReference@131245a
weak.get() = null
b = [B@15fbaa4

结论:从代码可以看出,只是通过软引用/弱引用再次引用的对象被回收了,而软引用/弱引用对象本身还是存在的。

问题二

第二个问题,怎么知道一个对象被回收线程处理过。
首先,被回收线程处理过的对象的引用对象被回收,通过get()方法获取到的一定是null。但是反过来,get()得到null的时候一定是被回收的吗?
code

package com.test.application;

import java.lang.ref.SoftReference;

public class References {

    public static void main(String[] args) {
        SoftReference<byte[]> soft1 = new SoftReference<>(new byte[1024 *1024 *6]);
        SoftReference<byte[]> soft2 = new SoftReference<>(new byte[1024 *1024 *6]);
        System.out.println("Soft1.get() = " + soft1.get() + "; soft1.isEnqueued() = " + soft1.isEnqueued());
        System.out.println("Soft2.get() = " + soft2.get() + "; soft2.isEnqueued() = " + soft2.isEnqueued());
        SoftReference<byte[]> soft3 = new SoftReference<>(new byte[1024 *1024 *12]);
        System.out.println("Soft1.get() = " + soft1.get() + "; soft1.isEnqueued() = " + soft1.isEnqueued());
        System.out.println("Soft2.get() = " + soft2.get() + "; soft2.isEnqueued() = " + soft2.isEnqueued());
        System.out.println("Soft3.get() = " + soft3.get() + "; soft3.isEnqueued() = " + soft3.isEnqueued());
        soft3.clear();
        System.out.println("Soft3.get() = " + soft3.get() + "; soft3.isEnqueued() = " + soft3.isEnqueued());
        SoftReference<byte[]> soft4 = new SoftReference<>(new byte[1024 *1024 *12]);
        System.out.println("Soft3.get() = " + soft3.get() + "; soft3.isEnqueued() = " + soft3.isEnqueued());
        System.out.println("Soft4.get() = " + soft4.get() + "; soft4.isEnqueued() = " + soft4.isEnqueued());
    }
}

result

Soft1.get() = [B@1b6d3586; soft1.isEnqueued() = false
Soft2.get() = [B@4554617c; soft2.isEnqueued() = false
[GC (Allocation Failure)  13915K->12932K(19968K), 0.0011343 secs]
[GC (Allocation Failure)  12932K->12980K(19968K), 0.0007815 secs]
[Full GC (Allocation Failure)  12980K->12884K(19968K), 0.0038280 secs]
[GC (Allocation Failure)  12884K->12884K(19968K), 0.0003637 secs]
[Full GC (Allocation Failure)  12884K->578K(19968K), 0.0036992 secs]
Soft1.get() = null; soft1.isEnqueued() = false
Soft2.get() = null; soft2.isEnqueued() = false
Soft3.get() = [B@74a14482; soft3.isEnqueued() = false
Soft3.get() = null; soft3.isEnqueued() = false
[GC (Allocation Failure)  13091K->13026K(19968K), 0.0005244 secs]
[Full GC (Ergonomics)  13026K->595K(19968K), 0.0032031 secs]
Soft3.get() = null; soft3.isEnqueued() = false
Soft4.get() = [B@1540e19d; soft4.isEnqueued() = false

从结论上看,soft1soft2因为内存不足被回收了,可以看到有回收的信息。但是soft3并没有被回收,此时对象new byte[1024 *1024 *8]仍然在内存中没有被回收,直到新的soft4创建的时候,soft3最开始指向的对象**new byte[1024 *1024 *8]**才被JVM回收。
因此,当调用clear()方法后,对象不一定会被回收,仍然会存在内存中(当然,最后总是会被回收掉的)。
更好的判断方式是引入引用队列。

public static void main(String[] args) {
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
        SoftReference<byte[]> soft1 = new SoftReference<>(new byte[1024 *1024 *6]);
        SoftReference<byte[]> soft2 = new SoftReference<>(new byte[1024 *1024 *6], queue);
        System.out.println("Soft1.get() = " + soft1.get() + "; soft1.isEnqueued() = " + soft1.isEnqueued());
        System.out.println("Soft2.get() = " + soft2.get() + "; soft2.isEnqueued() = " + soft2.isEnqueued());
        SoftReference<byte[]> soft3 = new SoftReference<>(new byte[1024 *1024 *12], queue);
        System.out.println("Soft1.get() = " + soft1.get() + "; soft1.isEnqueued() = " + soft1.isEnqueued());
        System.out.println("Soft2.get() = " + soft2.get() + "; soft2.isEnqueued() = " + soft2.isEnqueued());
        System.out.println("Soft3.get() = " + soft3.get() + "; soft3.isEnqueued() = " + soft3.isEnqueued());
        soft3.clear();
        System.out.println("Soft3.get() = " + soft3.get() + "; soft3.isEnqueued() = " + soft3.isEnqueued());
        SoftReference<byte[]> soft4 = new SoftReference<>(new byte[1024 *1024 *12], queue);
        System.out.println("Soft3.get() = " + soft3.get() + "; soft3.isEnqueued() = " + soft3.isEnqueued());
        System.out.println("Soft4.get() = " + soft4.get() + "; soft4.isEnqueued() = " + soft4.isEnqueued());
    }

Result

Soft1.get() = [B@1b6d3586; soft1.isEnqueued() = false
Soft2.get() = [B@4554617c; soft2.isEnqueued() = false
[GC (Allocation Failure)  13915K->12972K(19968K), 0.0007845 secs]
[GC (Allocation Failure)  12972K->12988K(19968K), 0.0005995 secs]
[Full GC (Allocation Failure)  12988K->12884K(19968K), 0.0037059 secs]
[GC (Allocation Failure)  12884K->12884K(19968K), 0.0004119 secs]
[Full GC (Allocation Failure)  12884K->578K(19968K), 0.0032263 secs]
Soft1.get() = null; soft1.isEnqueued() = false
Soft2.get() = null; soft2.isEnqueued() = true
Soft3.get() = [B@74a14482; soft3.isEnqueued() = false
Soft3.get() = null; soft3.isEnqueued() = false
[GC (Allocation Failure)  13092K->13026K(19968K), 0.0004341 secs]
[Full GC (Ergonomics)  13026K->595K(19968K), 0.0032196 secs]
Soft3.get() = null; soft3.isEnqueued() = false
Soft4.get() = [B@1540e19d; soft4.isEnqueued() = false

可以看出,soft2soft3的get()方法都是null, 但是soft2的对象是被因为被回收返回的null,而soft3则是自己主动断开了引用返回的null,并没有经历过回收的过程。

虚引用和引用队列

先看代码

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
        PhantomReference<byte[]> phantom = new PhantomReference<>(new byte[1024 * 1024 * 10], queue);
        System.out.println(phantom.get());
        System.out.println(phantom.isEnqueued());
        System.gc();
        Thread.sleep(100);
        System.out.println(phantom.get());
        System.out.println(phantom.isEnqueued());
        Thread.sleep(100); 
        byte[] b = new byte[11*1024*1024];
        System.out.println(b);
    }

执行结果:

null
false
[GC (System.gc())  12120K->10924K(19968K), 0.0009322 secs]
[Full GC (System.gc())  10924K->10865K(19968K), 0.0032936 secs]
null
true
[GC (Allocation Failure)  10865K->10865K(19968K), 0.0004269 secs]
[GC (Allocation Failure)  10865K->10865K(19968K), 0.0002253 secs]
[Full GC (Allocation Failure)  10865K->10861K(19968K), 0.0031404 secs]
[GC (Allocation Failure)  10861K->10861K(19968K), 0.0002806 secs]
[Full GC (Allocation Failure)  10861K->10843K(19968K), 0.0031496 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at com.hfxd.test.ReferenceQueueTest.main(ReferenceQueueTest.java:24)

从结果上看,有以下两点:

  1. 虚引用的引用对象不能通过get()方法来访问,任何时候访问返回的都是null。因此,通过null==get()的方式来判断引用对象是否已经被回收处理了是不一定准确的。
  2. 虚引用的对象在被回收线程gc()后,并没有被删除掉。查看虚引用的说明,说明如下:
Phantom reference objects, which are enqueued after the collector determines that their referents may otherwise be reclaimed.  Phantom references are most often used for scheduling pre-mortem cleanup actions in  a more flexible way than is possible with the Java finalization mechanism.
<p> If the garbage collector determines at a certain point in time that the referent of a phantom reference is <a href="package-summary.html#reachability">phantom reachable</a>, then at that time or at some later time it will enqueue the reference.
<p> In order to ensure that a reclaimable object remains so, the referent of a phantom reference may not be retrieved: The <code>get</code> method of a  phantom reference always returns <code>null</code>.
<p> Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued.  An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.

大概的意思就是,触发GC的时候,虚引用会被放入到引用队列中,并且将状态置为“已回收”。但是,引用的对象却不会被JVM回收。只有手动的把所有的引用断开,JVM才会回收这一部分的内存。
PS:更为详尽的例子可以参考NIO的实现。
上面的代码修改一下:

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
        PhantomReference<byte[]> phantom = new PhantomReference<>(new byte[1024 * 1024 * 10], queue);
        System.out.println(phantom.get());
        System.out.println(phantom.isEnqueued());
        System.gc();
        Thread.sleep(100);
        System.out.println(phantom.get());
        System.out.println(phantom.isEnqueued());
        Thread.sleep(100);
        Reference<byte[]> reference = (Reference<byte[]>) queue.poll();
        reference = null;
        phantom = null;
        byte[] b = new byte[11*1024*1024];
        System.out.println(b);
    }

手动将虚引用的对象置为null,内存中的数据不再存在引用,则被回收。

null
false
[GC (System.gc())  11867K->10864K(19968K), 0.0008976 secs]
[Full GC (System.gc())  10864K->10829K(19968K), 0.0032406 secs]
null
true
[GC (Allocation Failure)  11168K->10997K(19968K), 0.0007247 secs]
[GC (Allocation Failure)  10997K->10997K(19968K), 0.0002303 secs]
[Full GC (Allocation Failure)  10997K->621K(19968K), 0.0029060 secs]
[B@1b6d3586
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值