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 null.
 * 返回此引用(指向)的引用对象,如果引用对象被程序或者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 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 queue = new ReferenceQueue<>();
    SoftReference 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都会回收。弱引用通过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 null.
  * 返回此引用(指向)的引用对象,如果引用对象被程序或者GC清理,则返回null
  */
 public T get() {
     return this.referent;
 }

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

/**
 * 必须参数:-Xms5m -Xmx5m
 * 可选参数:-XX:+PrintGCDetails
 */
public void weakReferenceTest() {
    WeakReference 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
 * null.
 * 返回此引用的对象指向的实例,因为虚引用指向的实例总是不可达(无法访问),所以该方法总是返回null
 */
public T get() {
    return null;
}

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

public void phantomReferenceQueueTest() {
    ReferenceQueue queue = new ReferenceQueue<>();
    PhantomReference 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
Java中四种引用_第1张图片
2、选择要运行的项目/主类,在VM options中填入虚拟机参数,点击右下角Apply,再启动就行了。
Java中四种引用_第2张图片

你可能感兴趣的:(Java基础,Java虚拟机)