Java中四种引用

前言

在JDK1.2之前,对象的引用情况只有:引用未引用。从JDK1.2开始,引用状态分成了四种,从而更细粒度的控制对象的生命周期。四种引用由强到弱分别是:强引用软引用弱引用虚引用

引用

强引用(Strong Reference)

强引用简单点说就是平时使用最多的以一种引用,一般是使用new关键字实例化的引用:
Object object = new Object()
强引用的特点是:虚拟机宁愿抛出java.lang.OutOfMemoryError异常也不会回收强引用指向的实例。只有虚拟机关闭/终止了,强引用才会消失。如果想要打破这种强引用的关联,可以手动把引用设置为null
object = null
这样仅仅只是打破了强引用的关联关系,对象什么时候被回收,还得取决于虚拟机。如果对GC不太了解,可以参考我之前的博客:Java虚拟机——垃圾收集算法

软引用(Soft Reference)

软引用的强度比强引用弱,用来引用一种可有可无的对象,如果内存充足,gc便不会回收软引用所引用的对象。只有内存不足时,才会回收。
软引用可以用来实现缓存。
软引用通过java.lang.ref.SoftReference来实现,获取一个软引用指向的实例,使用SoftReference.get()方法

/**
 * Returns this reference object's referent.  If this reference object has
 * been cleared, either by the program or by the garbage collector, then
 * this method returns <code>null</code>.
 * 返回此引用(指向)的引用对象,如果引用对象被程序或者GC清理,则返回null
 */
public T get() {
    T o = super.get();
    if (o != null && this.timestamp != clock)
        this.timestamp = clock;
    return o;
}

根据JDK文档描述,可以使用SoftReference.get()来判断软引用指向的对象是否被回收(以下实例在运行时别忘了添加虚拟机参数,添加参数的方法写在的文章的末尾):

/**
 * 必须参数:-Xms5m -Xmx5m
 * 可选参数:-XX:+PrintGCDetails
 */
public void softReferenceTest() {
    SoftReference<byte[]> soft = new SoftReference(new byte[1024 * 1000]);
    // 此时内存空间充足,不会清理SoftReference指向的对象
    System.out.println(soft.get());
    byte[] bytes = new byte[1024 * 1024 * 3];
    // 通知GC执行垃圾回收
    System.gc();
    // 此时内存空间不足,清理SoftReference指向的对象
    System.out.println(soft.get());
}

输出结果

[B@372f7a8d
null

根据运行结果,可以看到SoftReference所引用的对象,在内存不足时,会被回收。
System.gc()通知虚拟机进行垃圾回收,但是虚拟机不一定会执行回收程序。可以添加虚拟参数-XX:+PrintGCDetails打印GC日志,这样就可以看到是否进行了GC。

除了使用SoftReference.get()方法,根据返回值是否为null来判断对象清理状态外,还可以通过结合java.lang.ref.ReferenceQueue来判断

/**
 * 必须参数:-Xms5m -Xmx5m
 * 可选参数:-XX:+PrintGCDetails
 */
public void softReferenceQueueTest() {
    ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
    SoftReference<byte[]> soft = new SoftReference(new byte[1024 * 1000], queue);
    // 软引用指向的实例没有被回收,因此不会被加入到ReferenceQueue,所以输出null
    System.out.println(queue.poll());
    byte[] bytes = new byte[1024 * 1024 * 3];
    // 通知GC执行垃圾回收
    System.gc();
    // 软引用指向的实例已经被回收了,会被加入到ReferenceQueue
    System.out.println(queue.poll());
}

输出结果

null
java.lang.ref.SoftReference@372f7a8d
弱引用(Weak Reference)

弱引用比软引用(Soft Reference)的强度更弱,也是用来引用一种可有可无的对象。和软引用不同的是,弱引用指向的对象不会等到内存不足时才会回收,而是每次GC都会回收。
弱引用一般在容器/工具(WeakHashMapThreadLocal)中使用,用于自动回收某些对象。
弱引用通过java.lang.ref.WeakReference来实现,同样利用WeakReference.get()方法来获取弱引用指向的实例,java.lang.ref.WeakReference并没有重写父类java.lang.ref.Reference中的get()方法(其实仔细看下Soft Reference中的get()方法也是调用父类的这个get()方法)

/**
  * Returns this reference object's referent.  If this reference object has
  * been cleared, either by the program or by the garbage collector, then
  * this method returns <code>null</code>.
  * 返回此引用(指向)的引用对象,如果引用对象被程序或者GC清理,则返回null
  */
 public T get() {
     return this.referent;
 }

同样的,我们可以利用get()方法来判断弱引用指向的实例是否被回收

/**
 * 必须参数:-Xms5m -Xmx5m
 * 可选参数:-XX:+PrintGCDetails
 */
public void weakReferenceTest() {
    WeakReference<byte[]> weak = new WeakReference<>(new byte[1024 * 1000]);
    System.out.println(weak.get());
    // 通知GC执行垃圾回收
    System.gc();
    // 此时内存空间充足,对象依然被回收了
    System.out.println(weak.get());
}

输出结果

[B@51887dd5
null

SoftReference一样,还可以通过结合java.lang.ref.ReferenceQueue来判断引用指向的实例是否被回收,代码基本和软引用一致,就不赘述了。

虚引用(Phantom Reference)

虚引用的强度比弱引用(Weak Reference)更弱,虚引用指向的实例可以说完完全全是无用对象。回收时间不确定,甚至无法被获取,作用仅仅是在被回收的时候收到一个系统通知。
虚引用主要是虚拟机开发人员用来管理堆外内存
根据软引用和弱引用的经验,我们可以试试用get()获取虚引用指向的实例,但是虚引用的get()方法定义如下:

/**
 * Returns this reference object's referent.  Because the referent of a
 * phantom reference is always inaccessible, this method always returns
 * <code>null</code>.
 * 返回此引用的对象指向的实例,因为虚引用指向的实例总是不可达(无法访问),所以该方法总是返回null
 */
public T get() {
    return null;
}

这也正好印证了我之前说的,虚引用指向的实例无法被获取,所以只能结合java.lang.ref.ReferenceQueue一起使用,这也是虚引用和软引用、弱引用不同的地方之一。

public void phantomReferenceQueueTest() {
    ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
    PhantomReference<byte[]> phantom = new PhantomReference<>(new byte[1024 * 1000], queue);
    System.out.println(queue.poll());
    // 通知GC执行垃圾回收
    System.gc();
    System.out.println(queue.poll());
}

多运行几次,结果总是不确定的,所以并不是GC之后,虚引用指向的实例就一定会被回收。

总结

回收时间作用生命周期
强引用不会被回收普通对象虚拟机关闭
软引用内存不足时高速缓存内存不足时,GC之后
弱引用GC时用于容器GC之后
虚引用-哨兵-

补充

如何添加虚拟机参数(以IDEA为例)
1、IDEA右上角,点击Edit Configurations
添加虚拟机参数
2、选择要运行的项目/主类,在VM options中填入虚拟机参数,点击右下角Apply,再启动就行了。
添加虚拟机参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值