Java中的引用类型

Java中的引用有点像C++中的指针,通过引用可以对堆中的对象进行操作。Java程序中最常见的引用类型是强引用,也是默认的引用类型。当在Java语言中使用 New 操作符创建一个新的对象,并将其赋给一个变量的时候,这个变量就成为指向该对象的一个强引用。

判断一个对象是否存活的标准为是否存在指向这个对象的指针。

Java中提供了四个级别的引用,即强引用(Strong Reference),软引用(Soft Reference),弱引用(Weak Reference),虚引用(Phantom Reference).在这4个级别的引用中只有强引用是包内可见的,其他3种引用类型均为Public,可以在应用程序中直接使用。

 

强引用:

我们平时声明的变量使用的就是强引用。如果一个对象被被人拥有强引用,那么垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。 

Java 的对象是位于 heap 中的,heap 中对象有强可及对象、软可及对象、弱可及对象、虚可及对象和不可到达对象。应用的强弱顺序是强、软、弱、和虚。对于对象是属于哪种可及的对象,由他的最强的引用决定。

如下代码:

String abc=new String("abc"); //1
SoftReference<String> softRef=new SoftReference<String>(abc); //2
WeakReference<String> weakRef = new WeakReference<String>(abc); //3
abc=null; //4
softRef.clear();//5

第一行在 heap 堆中创建内容为“abc”的对象,并建立 abc 到该对象的强引用,该对象是强可及的。
第二行和第三行分别建立对 heap 中对象的软引用和弱引用,此时 heap 中的 abc 对象已经有 3 个引用,显然此时 abc 对象仍是强可及的。
第四行之后 heap 中对象不再是强可及的,变成软可及的。
第五行执行之后变成弱可及的。


软引用:

如果一个对象只具有软引用,当内存空间足够时,GC就不会回收它,当内存空间不足时,JVM在抛出OOM之前,GC清理所有的软引用对象。只要GC没有回收软引用,该对象就可以被程序使用。类似弱引用,只不过JVM会尽量让软引用的存活时间长一些,迫不得已才清理。

软引用可用来实现内存敏感的高速缓存。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。

软引用是主要用于内存敏感的高速缓存。在 JVM 报告内存不足之前会清除所有的软引用,这样以来 GC就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。什么时候会被收集取决于 GC 的算法和 GC 运行时可用内存的大小。当 GC 决定要收集软引用时执行以下过程,以上面的 softRef 为例:

  1. 首先将 softRef 的 referent(abc)设置为 null,不再引用 heap 中的 new String("abc")对象。
  2. 将 heap 中的 new String("abc")对象设置为可结束的(finalizable)。
  3. 当 heap 中的 new String("abc")对象的 finalize()方法被运行而且该对象占用的内存被释放, softRef被添加到它的 ReferenceQueue(如果有的话)中。

注意: 

  • 对 ReferenceQueue 软引用和弱引用可以有可无,但是虚引用必须有。
  • 被 Soft Reference 指到的对象,即使没有任何 Direct Reference,也不会被清除。一直要到 JVM 内存不足且没有 Direct Reference 时才会清除,SoftReference 是用来设计 object-cache 之用的。如此一来SoftReference 不但可以把对象 cache 起来,也不会造成内存不足的错误 (OutOfMemoryError)。


弱引用(WeakReference)

如果一个对象只具有弱引用, 那该类就是可有可无的对象, 因为只要该对象被 GC 扫描到了随时都会把它干掉。

弱引用与软引用的区别在于:当GC在进行回收时,需要通过算法检查是否回收软引用对象,而对于弱引用对象总是进行回收。只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。

虽然GC在运行时一定回收弱引用对象,但复杂关系的若引用对象群常常需要好几次GC的运行才能完成。弱引用对象常常用于Map结构中,引用数据量较大的对象,一旦该对象的强引用为 null 时。GC能够快速的回收该对象空间。


虚引用(PhantomReference)

又称为“幽灵引用”,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动,主要目的是在一个对象所占的内存被实际回收之前得到通知,从而可以进行一些相关的清理工作。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

另外,建立虚引用之后通过 get 方法返回结果始终为 null, 通过源代码你会发现,虚引用通向会把引用的对象写进referent,只是 get 方法返回结果为 null,因此无法通过虚引用来获取被引用的对象。在虚引用中:

 

几种引用的比较 

  1. 不把 referent 设置为 null, 直接把 heap 中的 new String("abc")对象设置为可结束的(finalizable)。
  2. 与软引用和弱引用不同, 先把 PhantomRefrence 对象添加到它的 ReferenceQueue 中.然后在释放虚可及的对象。
引用类型回收时间用途
强引用永不回收普通对象引用
软引用内在不足回收缓存对象
弱引用垃圾回收时缓存对象
虚引用不确定不确定

 

finalization 机制

Java语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。Object类提供了finalize方法来添加自定义的销毁逻辑。如果一个类有特殊的销毁逻辑,可以复写finalize方法。

当GC发现没有引用指向一个对象时,会调用这个对象的finalize方法。通常在这个方法中进行一些资源释放和清理工作,如:关闭文件,套接字和数据库连接等。

由于 finalize 方法的存在,JVM中的对象一般处于三种可能的状态。第一种是可达状态,当有引用指向该对象时该对象处于可达状态。第二种是可复活状态,如果对象的类复写了 finalize 方法,则对象有可能处于该状态。虽然GC是在对象没有引用的情况下才调用其finalize方法,但是在finalize方法的实现中可能对当前对象添加新的引用。因此在finalize方法运行完成之后,GC需要重新检查该对象的引用。如果发现新的引用,那么对象回到可达状态,相当于该对象被复活,否则该对象会变成不可达状态。当对象从可复活状态变为可达状态后,对象会再次出现没有引用的状态。在这个情况下,finalize方法不会被再次调用,对象会直接变为不可达状态,也就是说,一个对象的finalize方法只会被调用一次。第三种状态是不可达状态,在这个状态下,GC可以自由的释放对象所占用的内存空间。

 

参考:Java 中对象的引用的四种级别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值