强引用、软引用、弱引用、幻象引用之间的区别和联系

强引用、软引用、弱引用、幻象引用之间的区别和联系

为什么需要四种引用

如果我们希望引用能够描述这样一类对象:当内存空间还足够时,就保留在内存之中,如果垃圾收集后内存空间比较紧张,那就抛弃这些对象释放空间。

对于上述的定义来说,一个对象只有 “被引用” 和 “未被引用” 两种状态,对这种情况显然是无能无力的。

四种引用类型

不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响。

  • 谓强引用(“Strong” Reference),就是我们最常见的普通对象引用,只要还有强引用指向一个对象,垃圾收集器不会碰这种对象,类似于User user = new User()。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为 null,就是可以被垃圾收集的了,当然具体回收时机还是要看垃圾收集策略。
  • 软引用(SoftReference),是一种相对强引用弱化一些的引用,可以让对象豁免一些垃圾收集,只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象。JVM 会确保在抛出 OutOfMemoryError 之前,清理软引用指向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。
  • 弱引用(WeakReference)并不能使对象豁免垃圾收集,仅仅是提供一种访问在弱引用状态下对象的途径。这就可以用来构建一种没有特定约束的关系,比如,维护一种非强制性的映射关系,如果试图获取时对象还在,就使用它,否则重现实例化。它同样是很多缓存实现的选择。
  • 对于幻象引用,有时候也翻译成虚引用,你不能通过它访问对象。幻象引用仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制,比如,通常用来做所谓的 Post-Mortem 清理机制,我在专栏上一讲中介绍的 Java 平台自身 Cleaner 机制等,也有人利用幻象引用监控对象的创建和销毁。

对象的生命周期

JDK1.2之前对象的生命周期:

在这里插入图片描述

JDK1.2后对象使用了四种引用类型,对象的生命周期如下所示:

在这里插入图片描述
这是 Java 定义的不同可达性级别(reachability level),具体如下:

  • 强可达(Strongly Reachable),就是当一个对象可以有一个或多个线程可以不通过各种引用访问到的情况。比如,我们新创建一个对象,那么创建它的线程对它就是强可达。
  • 软可达(Softly Reachable),就是当我们只能通过软引用才能访问到对象的状态。
  • 弱可达(Weakly Reachable),类似前面提到的,就是无法通过强引用或者软引用访问,只能通过弱引用访问时的状态。这是十分临近 finalize 状态的时机,当弱引用被清除的时候,就符合 finalize 的条件了。
  • 幻象可达(Phantom Reachable),上面流程图已经很直观了,就是没有强、软、弱引用关联,并且 finalize 过了,只有幻象引用指向这个对象的时候。

当然,还有一个最后的状态,就是不可达(unreachable),意味着对象可以被清除了。

我们可以在利用软引用和弱引用,将访问到的对象,重新指向强引用,也就是人为的改变了对象的可达性状态。

判断对象可达性,是 JVM 垃圾收集器决定如何处理对象的一部分考虑。

Reference类

所有的引用类型,都是抽象类 java.lang.ref.Reference 的子类,这个抽象类提供了 get 方法用来获取引用指向的对象(referent)

SoftReference<List<Foo>> ref =new SoftReference<List<Foo>>(newLinkedList<Foo>());
List<Foo>list = ref.get();

if (list!=null) {
  list.add(foo);
} else {
  //listisgone;dowhateverisappropriate
}

**对于幻象引用,我们无法通过幻象引用来取得一个对象实例,也就是说 get() 方法永远返回 null。**对于其他引用对象,如果还没有被销毁,都可以通过 get 方法获取原有对象。

引用队列

谈到各种引用的编程,就必然要提到引用队列ReferenceQueue ,引用队列主要用来配合引用工作。我们在创建各种引用并关联到响应对象时,可以选择是否需要关联引用队列,JVM 会在特定时机将引用 enqueue 到队列里,我们可以从队列里获取引用(remove 方法在这里实际是有获取的意思)进行相关后续逻辑。

其中幻象引用由于get 方法只返回 null,构造函数必须指定引用队列,因此必须和引用队列一起使用。

当某个被引用的对象(referent)被回收的时候,JVM 会将指向它的引用(reference)加入到引用队列的队列末尾,这可以作为一种通知机制。这个功能是由 ReferenceHandler 守护线程来做的,这个守护线程是在 Reference 静态代码块中建立并且运行的线程,所以只要 Reference 这个父类被初始化,该线程就会创建和运行。

我们可以通过 ReferenceQueue 中的元素(引用)来知道哪些对象(被引用的对象)被回收掉了,通过这种方式,我们就可以在对象被回收掉之后,做一些我们自己想做的事情。比如文章一开始提到的使用幻象引用监控对象的创建和销毁。

不同引用类型的应用场景

  • 软引用的应用:断路器
  • 弱引用的应用:ThreadLocal 的 ThreadLocalMap 实现
  • 幻象引用的应用:数据库连接池
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值