垃圾收集器与内存分配策略(一)之对象存活判断

垃圾收集器与内存分配策略(一)——对象存活判断

1、 概述

  在Java运行时内存区域划分中线程私有部分,虚拟机栈,本地方法栈,程序计数器3个区域随线程而生随线程而灭;其中虚拟机栈中的栈帧随方法的执行和结束进行着入栈和出栈操作,其中栈帧的内存是在类结构确定时已知的。因为方法结束或线程结束时,内存就回收了。所以这些区域不需要过多考虑回收问题。

  而对于Java堆和方法区来说,类或方法的分支需要的内存可能不一样,只有在程序运行时才知道会创建哪些对象,这部分内存的分配和回收是动态的,所以垃圾回收主要针对这块区域来说的。

2、 引用介绍

  在判断对象是否存活时都与引用有关。在JDK1.2之后有以下四种引用。

  1. 强引用(Strong Reference):强引用指的是在程序代码中普遍存在的,类似“Object obj = new Object()”这类引用,只要强引用还在,垃圾回收器永远不会回收掉任何被引用的对象。
  2. 软引用(Soft Reference):用来描述一些还有用但非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列入回收范围进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。JDK 1.2之后,提供了SoftReference类来实现软引用。
  3. 弱引用(Weak Reference):弱引用也是用来描述非必需对象的,但是他的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。JDK 1.2之后,提供了WeakReference类来实现弱引用。
  4. 虚引用(Phantom reference):一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象的实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

3、 对象是否存活

  堆里面几乎存放着Java中的所有对象,所以垃圾回收前对堆进行内存回收时,首先要判断对象是否”存活“。

3.1、 引用计数算法

  引用计数是一种简单判定效率较高的垃圾回收技术。每个对象都含有一个引用计数器,当有引用连接至对象时,引用计数加1.当引用离开作用域或引用被置为null时,引用计数减1.垃圾回收器将在含有全部对象的列表上遍历,当发现某个对象的引用计数为0时就立即释放该对象占用的空间(但是引用计数经常在引用计数为0时就立即释放对象)。

  缺陷:如果对象之间存在循环引用,可能出现对象应该被回收但引用计数却不为0的情况;对垃圾回收器来说,定位这种交互自引用的对象组所需的工作量极大。(并未被应用于任何一种Java虚拟机实现中)

3.2、 可达性分析算法

  通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象没有任何路径可以到达“GC Roots”时(用图论描述就是GC Roots到这个对象不可达),则证明此对象是不可用的。

  可作为“GC Roots”的对象有:

  1. 虚拟机栈中(栈帧中的本地变量表)引用的对象
  2. 本地方法栈JNI(即一般说的Native方法)中引用的对象
  3. 方法区中常量引用的对象
  4. 方法区中类静态属性引用的对象
   enter description here

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

  即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。
标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链。
   enter description here

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

  筛选的条件是此对象是否有必要执行finalize()方法。当前对象没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,对象会被回收,不进行下一次标记。

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

2.第二次标记

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

posted @ 2017-07-30 21:39  PrivateO2  阅读(225)  评论(0编辑  收藏  举报