Java中的引用类型

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jt102605/article/details/88638056

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 中对象的引用的四种级别

猜你喜欢

转载自blog.csdn.net/jt102605/article/details/88638056