JVM回顾---判断对象是否存活

判断对象是否存活分为两种方法:引用计数法和可达性分析法。

引用计数法

这种算法逻辑很简单,但是Java并不采用这种方法,因为这种方法存在致命的问题。

引用计数法的逻辑是:在堆中存储对象的时候,每个对象会有个对象头,对象头中维护了一个计数器counter,每当有地方引用到了这个对象就counter++,断开引用就counter—。如果counter为0,就代表没有地方引用了,就可以被GC了。

但是,如果对象A引用对象B,对象B引用对象A,然后没有任何其他的对象在引用他两了的时候,这样他两明明可以被GC,但是由于Counter永远等于1,导致这两个对象永远不会被回收。

可达性分析法

这种就是目前JVM使用的算法。他的逻辑就是先定义好了一系列对象,称之为GC Roots,然后就从GC Roots出发,向下搜索完所有的对象,如果发现某个对象到GCRoots没有任何引用链的话,就证明这个对象是不可用的,就可以被回收了。哪怕上面的A和B那种情况,只有A和B没发到达GC Roots,那么就会被回收。

GC Roots:

  • 虚拟机栈(栈桢中的本地变量表)中的引用的对象
  • 方法区中的类静态属性引用的对象
  • 方法区中的常量引用的对象
  • 本地方法栈中JNI(Native方法)的引用的对象

生存还是死亡

尽管在可达性分析之后,这个对象不可达,但是也不代表这个对象非死不可,这个时候处于一个缓刑状态,他在死之前仍然还会进行两次标记:

  1. 如果可达性分析之后,发现这个对象没有与GC Roots的引用链,那么这个时候就会被第一次标记并且进行筛选,筛选的条件是这个对象有没有必要执行finalize方法,如果这个对象没有覆盖finalize方法或者已经执行过了,那就视为没有必要,那么就会死亡
  2. 如果覆盖了finalize方法,这个对象就会被放入一个称为F-Queue的队列中,稍后由一个虚拟机自动创建的、低优先级的Finalizer线程去执行他,这个时候就会触发这个方法,但是并不代表虚拟机会承诺让他的finalize 方法执行完毕。这是因为如果有某个对象finalize执行时间过长的话,那么会影响到后面所有的对象的finalize执行,导致整个内存回收系统崩溃。

所以说,finalize方法是对象逃脱死亡的最后一次机会,只要他重写了finalize方法,并且将它重新与GC Roots的任意一条引用链搭上,那么就不会被杀死。这个时候在第二次标记的时候就会把它从即将回收的集合中移除,如果这个时候这个对象还没有逃脱,那就证明他真的可以被回收了。

猜你喜欢

转载自blog.csdn.net/why1092576787/article/details/114703013