java 可达性算法实现,可达性算法、Java引用 详解

在堆里存放着几乎多有的java对象实例,垃圾搜集器在对堆进行回收之前,第一件事情就是确定这些对象之中哪些还“存活”着(即通过任何途径都无法使用的对象)。

在堆里存放着几乎多有的java对象实例,垃圾搜集器在对堆进行回收之前,第一件事情就是确定这些对象之中哪些还“存活”着(即通过任何途径都无法使用的对象)。

一、可达性分析算法

在Java中,是通过可达性分析(Reachability Analysis)来判定对象是否存活的。该算法的基本思路就是通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到GC Roots没有任何引用链相连时(即从GC Roots节点到该节点不可达),则证明该对象是不可用的。

48ca0c057940

可达性分析算法判断对象是否可以回收

如上图所示,object1~object4对GC Root都是可达的,说明不可被回收,object5和object6对GC Root节点不可达,说明其可以被回收。

在Java中,可作为GC Root的对象包括以下几种:

虚拟机栈(栈帧中的本地变量表)中引用的对象

方法区中类静态属性引用的对象

方法区中常量引用的对象

本地方法栈中JNI(即一般说的Native方法)引用的对象

二、finalize()方法最终判定对象是否存活

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。

标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链。

1. 第一次标记并进行一次筛选。

筛选的条件是此对象是否有必要执行finalize()方法。

当对象没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,对象被回收。

2. 第二次标记

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象finalize()方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。

Finalize()方法是对象脱逃死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中成功拯救自己----只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。

三、Java引用

从可达性算法中可以看出,判断对象是否可达时,与“引用”有关。那么什么情况下可以说一个对象被引用,引用到底代表什么?

在JDK1.2之后,Java对引用的概念进行了扩充,可以将引用分为以下四类:

强引用(Strong Reference)

软引用(Soft Reference)

弱引用(Weak Reference)

虚引用(Phantom Reference)

这四种引用从上到下,依次减弱

3.1 强引用

强引用就是指在程序代码中普遍存在的,类似Object obj = new Object()这类似的引用,只要强引用在,垃圾搜集器永远不会搜集被引用的对象。也就是说,宁愿出现内存溢出,也不会回收这些对象。

3.2 软引用

软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。

import java.lang.ref.SoftReference;publicclassMain{publicstaticvoidmain(String[]args){SoftReferencesr=newSoftReference(newString("hello"));System.out.println(sr.get());}}

3.3 弱引用

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:

import java.lang.ref.WeakReference;publicclassMain{publicstaticvoidmain(String[]args){WeakReferencesr=newWeakReference(newString("hello"));System.out.println(sr.get());System.gc();//通知JVM的gc进行垃圾回收System.out.println(sr.get());}}

3.4 虚引用

虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。

要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

importjava.lang.ref.PhantomReference;importjava.lang.ref.ReferenceQueue;publicclassMain{publicstaticvoidmain(String[]args){ReferenceQueuequeue=newReferenceQueue();PhantomReferencepr=newPhantomReference(newString("hello"),queue);System.out.println(pr.get());}}

3.5 软引用和弱引用进一步说明

在SoftReference类中,有三个方法,两个构造方法和一个get方法(WekReference类似):

publicclassSoftReferenceextendsReference{/**

* Timestamp clock, updated by the garbage collector

*/staticprivate long clock;/**

* Timestamp updated by each invocation of the get method.  The VM may use

* this field when selecting soft references to be cleared, but it is not

* required to do so.

*/private long timestamp;/**

* Creates a new soft reference that refers to the given object.  The new

* reference is not registered with any queue.

*

* @param referent object the new soft reference will refer to

*/publicSoftReference(T referent){super(referent);this.timestamp=clock;}/**

* Creates a new soft reference that refers to the given object and is

* registered with the given queue.

*

* @param referent object the new soft reference will refer to

* @param q the queue with which the reference is to be registered,

*          or null if registration is not required

*

*/publicSoftReference(T referent,ReferenceQueue<?superT>q){super(referent,q);this.timestamp=clock;}/**

* Returns this reference object's referent.  If this reference object has

* been cleared, either by the program or by the garbage collector, then

* this method returns null.

*

* @return  The object to which this reference refers, or

*          null if this reference object has been cleared

*/public Tget(){T o=super.get();if(o!=null&&this.timestamp!=clock)this.timestamp=clock;returno;}}

get方法用来获取与软引用关联的对象的引用,如果该对象被回收了,则返回null。

在使用软引用和弱引用的时候,我们可以显示地通过System.gc()来通知JVM进行垃圾回收,但是要注意的是,虽然发出了通知,JVM不一定会立刻执行,也就是说这句是无法确保此时JVM一定会进行垃圾回收的。

3.6 虚引用进一步说明:

虚引用中有一个构造函数,可以看出,其必须和一个引用队列一起存在。get()方法永远返回null,因为虚引用永远不可达。

publicclassPhantomReferenceextendsReference{/**

* Returns this reference object's referent.  Because the referent of a

* phantom reference is always inaccessible, this method always returns

* null.

*

* @return  null

*/public Tget(){returnnull;}/**

* Creates a new phantom reference that refers to the given object and

* is registered with the given queue.

*

*

It is possible to create a phantom reference with a null

* queue, but such a reference is completely useless: Its get

* method will always return null and, since it does not have a queue, it

* will never be enqueued.

*

* @param referent the object the new phantom reference will refer to

* @param q the queue with which the reference is to be registered,

*          or null if registration is not required

*/publicPhantomReference(T referent,ReferenceQueue<?superT>q){super(referent,q);}}

参考

《深入理解Java虚拟机》

作者:kylinxiang

链接:https://www.jianshu.com/p/8f5fa8288d9b

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值