对象死了吗?

哪些区域需要回收

运行时内存分为5个区域:程序计数器、虚拟机栈、本地方法栈、堆、方法区,这些区域是如何回收的?

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

垃圾收集器在对堆进行回收前,需要先判断堆中哪些对象是“活着”的,哪些对象是“死了”的。

1-程序计数器、虚拟机栈、本地方法栈

这3个区域是随着线程而生,随着线程而灭,栈中的栈帧数据随着方法的调用到结束有条不紊的进行着入栈出栈,每一个栈帧中分配多少内部基本上在类结构确定下来就已知了。内存分配和回收具有确定性,随着方法的结束或者线程的结束后,内存自然就会被回收了。

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

1.引用计数法

2- java堆和方法区

一个接口中的多个实现类需要的内存可能不一样,一个方法的多个分支需要的内存也不一样,我们只有在程序处于执行期间才能知道会创建哪些对象,对这部分的内存分配和回收是动态的,垃圾回收主要关注的是这部分的内存

永利国际网站 1可达性分析算法判断对象是否可以回收如上图所示,object1~object4对GC
Root都是可达的,说明不可被回收,object5和object6对GC
Root节点不可达,说明其可以被回收。在Java中,可作为GC
Root的对象包括以下几种:

  给对象添加一个引用计数器,每当有一个地方引用它时,计数器加1,当引用失效时,计数器减1。任何时刻计数器为0的对象就是不可能再被使用的。

判断对象是“生”是“死”?

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

  引用计数器实现简单,效率高。但是主流的JVM并没有使用引用计数法来管理内存,主要原因是它难以解决对象之间循环引用的问题。例如,对象objA和对象objB都有字段instance,令objA.instance=objB,objB.instance=objA,除此之外这两个对象再无任何引用,实际上这两个对象再无可能被访问,但是因为两个对象之间互相引用,两者的引用计数器都不为0,引用计数法无法回收这两个对象。

引用计数算法

java虚拟机没有采用引用计数算法来判断对象是否生存,主要考虑的是它很难解决对象之间的相互循环引用的问题。

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。永利国际网站 ,标记的前提是对象在进行可达性分析后发现没有与GC
Roots相连接的引用链。1.
第一次标记并进行一次筛选。
筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,对象被回收。2.
第二次标记
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象finalize()方法中执行缓慢,或者发生死循环,将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。Finalize()方法是对象脱逃死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中成功拯救自己—-只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。

2.可达性分析算法

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website