JAVA--------对强引用,软引用,弱引用和虚引用的理解

引用出现的根源是由于GC内存回收的基本原理—GC回收内存本质上是回首对象,而目前比较流行的回收算法是可达性分析算法,从GC Roots开始按照一定的逻辑判断一个对象是否可达,不可达的话就说明这个对象已死(除此之外另外一种常见的算法就是引用计数法,但是这种算法有个问题就是不能解决相互引用的问题)。基于此Java向用户提供了四种可用的引

这些类实现的核心是Reference与ReferenceQueue(更通俗地说引用队列)两个类,而且这两个类也特别的简单。Reference类似一个链表结构,通过创建一个守护线程来执行对应引用的清除、Cleaner.clean(如果传入的对象是该类的话)、以及引用
的入队操作(需要在创建引用的时候制定一个引用队列);ReferenceQueue这是制定了引用队列的一些具体操作,简单的来说它也是一个链表结构,并提供了一些基本的链表操作)。而除了强引用外其它的都是继承于此,通过这样的类约束了引用的相关内容,便于和GC进行交互。这几种引用的区别如下:

强引用(Strong Reference)

  • 强引用就是普通对象引用,只要还有一个强引用指向一个对象,就表示对象还活着,此时jvm也不会对强引用对象进行垃圾回收。对于一个普通对象,如果没有其他引用的话,只要超过了对象的作用域或者显示地将强引用赋值为null,就可以进行垃圾回收,但是具体的回收时间由垃圾回收策略决定。
  • 平时典型的强引用就是 Object obj=new Object() ,只要通过new创建的对象就是强引用,当JVM内存不足的时候,JVM宁愿抛出OutOfMemoryError,使程序异常终止也不会回收具有强引用对象,来解决内存不够的问题。在JVM中只要强引用还在,垃圾回收永远不会回收此对象实例

 

软引用(Soft Reference) 

 JDK1.2之后提供SoftReference来实现软引用。

  • 软引用使相对于强引用弱一些的引用,他可以避免对象进行一些垃圾回收。所以软引用对象的生命周期比强引用短一些。只有当JVM认为内存不足的时候才会对软引用对象进行垃圾回收。JVM在确保OUtOfMemoryError之前,会将所有对象进行垃圾回收,即清理软引用对象。软引用通常用来使用内存敏感的缓存,如果还有空闲的内存,就可以占时保留软引用对象,但是当内存不足的时候会清理软引用对象,这样就保证了使用缓存的同时,不会耗尽内存。
  • 软引用可以和一个引用队列(Reference Queue)联合使用,如果软引用对象所引用的对象被垃圾回收,Java虚拟机就会把该软引用对象加入到与之关联的引用队列中。然后可以继续调用Reference Queue的poll()方法来检查是否有与他关联的对象被回收。如果队列为空,讲返回一个null ,否则该方法返回队列中前边的一个Reference对象。

应用场景:

  • 软引用长用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不够的时候就把缓存清理掉,就保证了使用缓存的同时,不会耗尽内存。

 

弱引用(Soft Reference)     

JDK1.2之后使用WeakReference来实现 弱引用。

  • 仅被弱引用关联的对象最多只能生存到下一次GC开始之前。当垃圾回收开始工作时,无论当前内存是否够用,都会回收掉仅被弱引用关联的对象。
  • 他通过WeakReference类实现,他的生命周期比软引用生命周期短。在垃圾回收器线程扫描它所管理的内存区域中,一旦发现有弱引用对象,不管内存够不够,都会对其进行回收。由于垃圾回收线程是一个优先级很低的线程,因此不一定会立即回收弱引用对象。弱引用可以和一个引用队列联合使用,如果弱引用对象被垃圾回收,Java虚拟机就会把该引用加入到与之关联的队列中。它不能避免垃圾回收,仅仅提供一种访问在弱引用状态下对象的途径。这就可以来构建一种没有特定约束的关系。比如,维护一种非强制性的映射关系了,如果试图获取的对象还在,就使用它,如果不在就重新实例化对象。它是多种缓存实现的选择。

应用场景:

  • 适用于内存敏感的缓存

虚引用(Phantom Reference)    

在JDK1.2之后,提供了PhantomReference类来实现虚引用

  • 无法通过虚引用来访问对象的任何属性或者函数。他只提供了一致确保对象在finalize以后,做事情的机制,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收,虚引用必须和引用队列联合使用,当垃圾回收期回收一个对象时,如果发现他有虚引用,就会在垃圾回收内存前,把这个虚引用加入到与之关联的引用队列中。比如,Java平台的cleaner机制,也有人利用虚引用来监视对象的创建和销毁。

ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue); 

  • 程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取一些程序行动
  • 虚引用也被称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

应用场景:

  • 可以跟踪对象被立即回收的活动,当一个虚引用关联的对象被垃圾回收器回收之前收到一条通知。


 

对象可达性状态流转分析:

JAVA--------对强引用,软引用,弱引用和虚引用的理解_第1张图片

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

所有引用类型,都是抽象类 java.lang.ref.Reference 的子类,该类提供了get()方法

T                       get()                                  Return  this  reference  object's referent

这里面除了虚引用(因为虚引用get()方法永远返回null),如果对象还没有被销毁,都可以通过get()方法获取原有随心,即我我们可以通过软引用和弱引用访问到对象,重新执行强引用,也就是人为的改变了对象的可达性状态,所以画的双向状态。

所以,对于软引用和弱引用,垃圾收集器可能存在二次确认,以保证处于弱引用状态的对象没有改变为强引用。

缺点:

如果错误的保持了强引用(比如,赋值给static变量),那么对象可能就没有机会变回类似弱引用的可达性状态,就会产生内存泄漏。所以,检查弱引用指向对象是否垃圾回收,以为是诊断是否有内存泄漏的一个思路。

引用队列(Reference Queue)

我们在创建各种引用并关联到响应对象时,可以选择是否需要关联引用队列,JVM 会在特定时机将引用 enqueue 到队列里,我们可以从队列里获取引用(remove 方法在这里实际是有获取的意思)进行相关后续逻辑。尤其是幻象引用,get 方法只返回 null,如果再不指定引用队列,基本就没有意义了利用引用队列,我们可以在对象处于相应状态时(对于幻象引用,就是前面说的被 finalize 了,处于幻象可达状态),执行后期处理逻辑。

诊断JVM引用情况

如果你怀疑应用存在引用(或 finalize)导致的回收问题,可以有很多工具或者选项可供选择,
比如 HotSpot JVM 自身便提供了明确的选项(PrintReferenceGC)去获取相关信息。
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintReferenceGC

 

 

你可能感兴趣的:(java36)