[解密JVM-15] 垃圾回收:引用的分类

1、前言

  无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判断对象是否存活,都和“引用”离不开关系。

  在 JDK1.2 以前, Java 里面的引用是很传统的定义:如果 reference 类型的数据中存储的数值代表的是另一块内存的起始地址,就称该 reference 数据是代表某块内存、某个对象的引用。

  一个对象在上面的定义下,只有“被引用”和“不被引用”两种状态,对于描述一些“食之无味、弃之可惜”的对象就显得无能为力。比如说,有一些对象,当内存空间充足的时候,它最好保留在内存里,当内存不足的时候,抛弃他们也没多大关系。其实说的就是缓存。

  在 JDK1.2 之后,Java 对引用的概念进行了扩充,把引用分为:强引用、软引用、弱引用和虚引用。

  然而,我们开发中看到的基本上 99% 都是强引用,其他三个都是在很特殊的场景下才会用。但是!面试必问。

  我们可以在java.lang.ref包下找到类Reference

2、强引用(Strong Reference)

  在 Java 程序中,最常见的引用类型就是强引用,也是默认的引用类型。

  当 Java 语言中使用 new 操作符创建一个新的对象,并将其赋值给一个变量的时候,这个变量就称为指向该对象的一个强引用。

  强引用的对象是可触及的,垃圾收集器永远不会回收掉被引用的对象。

  对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显示地将引用赋值为 null,就是可以当做垃圾被收集了,当然具体回收实际还是看垃圾收集策略。

  相对的,软引用、弱引用和虚引用的对象是软可触及、弱可触及和虚可触及的,在一定条件下,都是可以被回收的。所以,强引用是造成 Java 内存泄漏的主要原因之一。

  比如一个例子:

StringBuffer str = new StringBuffer("学哥斌");

  局部变量 str 指向 StringBuffer 实例所在的堆空间,通过 str 可以操作该实例,那么 str 就是 StringBuilder 实例的强引用。
在这里插入图片描述
  此时,再运行一个赋值语句:

StringBuffer str1 = str;

  就会变成如下:
在这里插入图片描述
  str 和 str1 都是强引用,可以通过该引用访问目标对象,所引用的对象在任何时候都不会被系统回收,虚拟机宁愿 OOM 也不会回收它。

3、软引用(Soft Reference)

  软引用是用来描述一些还有用,但是非必要的对象。只被软引用关联着的对象,在系统将要发生内存溢出的异常前,会把对象列进回收范围之中进行第二次回收,即:先回收没有引用的对象,回收后如果空间够了,软引用就不被回收;如果第一次回收掉没引用的对象后,空间还是不足,那么就会回收软引用的对象;如果软引用回收了还是空间不足,就报 OOM。

  软引用通常用来实现内存敏感的缓存。比如:高速缓存就有用到软引用。如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。

  垃圾回收器在某个时刻决定回收软可达的对象的时候,会清理软引用,并可选地把引用存放到一个引用队列(Reference Queue)。

  类似弱引用,只不过 Java 虚拟机会尽力让软引用的存活时间长一点,迫不得已才清理。

  在 JDK1.2 之后提供了java.lang.ref.SoftReference类来实现软引用。

Object obj = new Object();	// 生命强引用

SoftReference<Object> sf = new SoftReference<Object>(obj);

obj = null;	// 销毁强引用

  经过上面三步后,第一行 new 出来的对象就是弱引用对象,当内存不足时,是会被清理掉的。

4、弱引用(Weak Reference)

  弱引用也是用来描述非必需对象,被弱引用关联的对象一旦遇到 GC,无论内存是否足够都要被回收。

  由于垃圾回收器的线程通常优先级很低,因此,并不一定能很快发现持有弱引用的对象。在这种情况下,弱引用对象可以存在较长的时间。

  弱引用和软引用一样,在构造弱引用时,也可以制定一个引用队列,当弱引用对象被回收时,就会假如指定的引用队列,通过这个队列可以跟踪对象的回收情况。

  软引用、弱引用都非常适合来保存那些可有可无的缓存数据,如果这么做,当系统内存不足时,这些缓存数据就会被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间,从而起到加速系统的作用。

  在 JDK1.2 以后提供了java.lang.ref.WeakReference类来实现弱引用。

Object obj = new Object();	// 声明强引用
WeakReference<Object> wr = new WeakReference<Object>(obj);
obj = null;	// 销毁强引用

  弱引用对象与软引用对象的最大不同就在于,当 GC 在进行回收时,需要通过算法检查是否回收软引用对象,而对于弱引用对象,GC 总是进行回收。弱引用对象更容易、更快被 GC 回收。

  比如java.util.WeakHashMap,它的底层就是使用了java.lang.ref.WeakReference
在这里插入图片描述
  被这个WeakHashMap引用的对象很适合作为缓存,当系统执行 GC 就被回收了。

5、虚引用(Phantom Reference)

  虚引用是引用类型最弱的一个,弱到发虚,为什么说它虚呢,是因为想通过它来访问对象都做不到,跟没有引用一样,所以说它“虚”。

  一个对象是否有虚引用,完全不会决定对象的生命周期。如果一个对象仅持有虚引用,那它很没有引用几乎是一样的,随时被垃圾回收器回收。

  虚引用不能单独使用,也不能用来访问对象,当试图使用虚引用的get()方法来取得对象时,总是 null。

  那虚引用有啥用呢?

  为一个对象设置虚引用关联的唯一目的在于跟踪垃圾回收过程。比如:能在这个对象被收集器回收时收到一个系统通知。

  虚引用必须和引用队列一起使用,这一点可以通过它的构造函数了解到:
在这里插入图片描述
  当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入到引用队列,已通知应用程序对象的回收情况。

  由于虚引用可以跟踪对象的回收时间,因此,也可以将一些资源释放操作放置在虚引用中执行和记录。

Object obj = new Object();	// 声明强引用
ReferenceQueue phantomQueue = new ReferenceQueue();
PhantomReference<Object> pf = new PhantomReference<Object>(obk, phantomQueue);
obj = null;	// 去除强引用
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值