垃圾回收机制

Catalogue   

对象存活

GC算法

运行时数据区

可达性分析与引用计数

引用计数法

给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1;当引用失效时,计数器就-1;任何时刻计数器为 0 的对象就是不能再被使用的,即对象已“死”。
在主流的 JVM 中没有选用引用计数法来管理内存,最主要的原因是引用计数法无法解决对象的循环引用问题。

可达性分析算法

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

对象 Object5 —Object7 之间虽然彼此还有联系,但是它们到 GC Roots 是不可 达的,因此它们会被判定为可回收对象。

在 Java 语言中,可作为 GC Roots 的对象包含以下几种:

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

在 JDK1.2 之后,Java 对引用的概念做了扩充,将引用分为强引用(StrongReference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用 (Phantom Reference)四种,这四种引用的强度依次递减。

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

如果对象在进行可达性分析之后发现没有与 GC Roots 相连接的引用链,那它 将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。
当对象没有覆盖finalize()方法或者finalize()方法已经被 JVM 调 用过,虚拟机会将这两种情况都视为”没有必要执行”,此时的对象才是真正” 死”的对象。

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在 一个叫做 F-Queue 的队列之中,并在稍后由一个虚拟机自动建立的、低优先级的
Finalizer 线程去执行它(这里所说的执行指的是虚拟机会触发finalize() 方法)。finalize()方法是对象逃脱死亡的最后一次机会,稍后 GC 将对 F-Queue
中的对象进行第二次小规模标记,如果对象在finalize()中成功拯救自己(只需 要重新与引用链上的任何一个对象建立起关联关系即可),那在第二次标记时
它将会被移除出”即将回收”的集合;如果对象这时候还是没有逃脱,那基本上它就是真的被回收了。

回收方法区

方法区(永久代)的垃圾回收主要收集两部分内容:废弃常量和无用类。
回收废弃常量和回收 Java 堆中的对象十分类似。以常量池中字面量(直接量)的回 收为例,假如一个字符串”abc”已经进入了常量池中,但是当前系统没有任何
一个 String 对象引用常量池中的”abc”常量,也没有其他地方引用这个字面量,如果此时发生 GC 并且有必要的话,这个”abc”常量会被系统清理出常量池。
常量池中的其他类(接口)、方法、字段的符号引用也与此类似。

判定一个类是否是”无用类”则相对复杂很多。类需要同时满足下面三个条件才会 被算是”无用的类”。

  1. 该类的所有实例都已经被回收(即在 Java 堆中不存在任何该类的实例)
  2. 加载该类的 ClassLoader 已被回收
  3. 该类对应的 Class 对象没有任何其他地方被引用,无法在任何地方通过反射访问该类的方法

垃圾回收算法

标记-清除算法

复制算法(新生代回收算法)

标记整理算法(老年代回收算法)

分代收集算法