(GC)垃圾标记算法详解攻略

目录
  • 引用计数法
  • 引用链法(可达性分析法)
  • 总结
一、简介
  • 垃圾收集器对 Java堆里的对象 是否进行回收的判断准则:Java对象是存活 or 死亡

判断对象为死亡才会进行回收

  • 在Java虚拟机中,判断对象是否存活有2种方法:
  1. 引用计数法
  2. 引用链法(可达性分析法)

这两个算法都和引用有些关联,因此讲垃圾标记算法前,我们先回顾一下引用的知识点。

1、Java 中的引用

在JDK1.2之后, Java将引用分为强引用、软引用、弱引用和虚引用。

1.1 强引用

当我们新建一个对象时就创建了一个具有强引用的对象,如果一个对象具有强引用,垃圾收集器就绝不会回收它。Java虚拟机宁愿抛出OutOfMemoryError异常,使程序异常终止,也不会回收具有强引用的对象来解决内存不足的问题。

1.2 软引用

如果一个对象只具有软引用,当内存不够时,会回收这些对象的内存,回收后如果还是没有足够的内存,就会抛出OutOMemoryError异常。Java提供了SofReference类来实现软引用。

1.3 弱引用

弱引用比起软引用具有更短的生命周期,垃圾收集器一旦发现了只具有弱引用的对象,不管当前内存是否足够,都会回收它的内存。Java提供了WeakReference类来实现弱引用。

1.4 虚引用

虚引用并不会决定对象的生命周期,如果一个对象仅持有虚引用,这就和没有任何引用一样,在任何时候都可能被垃圾收集器回收。一个只具有虚引用的对象,被垃圾收集器回收时会收到一个系统通知,这也是虚引用的主要作用。Java提供了PhantomReference类来实现虚引用。

接下来分别介绍垃圾比较算法。

二、引用计数法
1、方式描述
  • 给 Java 对象添加一个引用计数器
  • 每当有一个地方引用它时,计数器 +1;引用失效则 -1;
2、判断对象存活准则
  • 当计数器不为 0 时,判断该对象存活;否则判断为死亡(计数器 = 0)。
3、优点
  • 实现简单
  • 判断高效
4、缺点
  • 无法解决 对象间相互循环引用 的问题

即该算法存在判断逻辑的漏洞

  • 具体描述
<-- 背景 -->
// 对象objA 和 objB 都有字段 name
// 两个对象相互进行引用,除此之外这两个人对象没有任何引用
objA.name = objB;
objB.name = objA;

<-- 问题 -->
// 实际上这两个对象已经不可能再被访问,应该要被垃圾收集器进行回收
// 但因为他们相互引用,所以导致计数器不为0,这导致引用计数算法无法通知垃圾收集器回收该两个对象

正由于该算法存在判断逻辑漏洞,所以 Java虚拟机没有采用该算法判断Java是否存活。

三、引用链法(可达性分析法)
  • 很多主流商用语言(如Java、C#)都采用 引用链法 判断 Java对象是否存活。
  • 含3个步骤:
  1. 可达性分析
  2. 第一次标记 & 筛选
  3. 第二次标记 & 筛选
1、可达性分析
a. 方式描述

将一系列的 GC Roots 对象作为起点,从这些起点开始向下搜索。

  • 可作为 GC Root 的对象有:
  1. Java虚拟机栈(栈帧的本地变量表)中引用的对象
  2. 本地方法栈 中 JNI引用对象
  3. 方法区 中常量、类静态属性引用的对象
  • 向下搜索的路径 = 引用链

如下图:
在这里插入图片描述

b. 判断 对象是否可达 标准

当一个对象到 GC Roots 没有任何引用链相连时,则判断该对象不可达

没有任何引用链相连 = GC Root到对象不可达 = 对象不可用

在这里插入图片描述

特别注意
  • 可达性分析 仅仅只是判断对象是否可达,但还不足以判断对象是否存活 / 死亡
  • 当在 可达性分析 中判断不可达的对象,只是“被判刑” = 还没真正死亡

不可达对象会被放在”即将回收“的集合里。

  • 要判断一个对象真正死亡,还需要经历两个阶段:
  1. 第一次标记 & 筛选
  2. 第二次标记 & 筛选
2、第一次标记 & 筛选
  • 对象 在 可达性分析中 被判断为不可达后,会被第一次标记 & 准备被筛选
  1. 不筛选:继续留在 ”即将回收“的集合里,等待回收;
  2. 筛选:从 ”即将回收“的集合取出
  • 筛选的标准:该对象是否有必要执行 finalize()方法
  1. 若有必要执行(人为设置),则筛选出来,进入下一阶段(第二次标记 & 筛选);
  2. 若没必要执行,判断该对象死亡,不筛选 并等待回收;
    当对象无 finalize()方法 或 finalize()已被虚拟机调用过,则视为“没必要执行”
3、第二次标记 & 筛选

当对象经过了第一次的标记 & 筛选,会被进行第二次标记 & 准备被进行 筛选

a. 方式描述

该对象会被放到一个 F-Queue 队列中,并由 虚拟机自动建立、优先级低的Finalizer 线程去执行 队列中该对象的finalize()

  • finalize()只会被执行一次
  • 但并不承诺等待finalize()运行结束。这是为了防止 finalize()执行缓慢 / 停止 使得 F-Queue队列其他对象永久等待。
b. 筛选标准

在执行finalize()过程中,若对象依然没与引用链上的GC Roots 直接关联 或 间接关联(即关联上与GC Roots 关联的对象),那么该对象将被判断死亡,不筛选(留在”即将回收“集合里) 并 等待回收

四、总结

整体标记流程:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值