JVM 系列文章之 对象存活分析 - 引用计数 and 可达性分析

Catalogue
  1. 1. 前言
  2. 2. 引用计数法
  3. 3. 可达性分析算法
    1. 3.1. 哪些引用是 GC Roots
    2. 3.2. Java可达性分析算法会不会出现循环引用问题?
  4. 4. 小结
  5. 5. 参考资料 & 鸣谢

前言

在垃圾回收器回收对象时,我们如何判断哪些对象是”活”的,哪些是”死”的,关于判断对象存活的方法主要分为两类: 引用计数法 和 可达性分析算法

引用计数法

引用计数器就是: 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减一;任何时刻计数器为 0 的对象就是不可能再被使用的,可以此时进行回收。

但是引用计数法有一个很大的缺陷,就是它很难解决对象之间相互循环引用的问题。

可达性分析算法

在主流的商用程序语言(Java,C#等)的主流实现中,都是称通过可达性分析来判断对象是否存活的

R大在知乎上有关可达性分析算法的回答是:

Tracing GC 的根本思路就是: 给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可达到的)对象就被判定为存活,其余对象 (也就是没有被比遍历到的)就自然被判定为死亡,注意的是: tracing GC的本质是通过找出所有活对象来把其余空间认定为”无用”,而不是找出所有死掉的对象并回收它们占用的空间

这里的集合的引用就是 “GC Roots”,所谓 “GC Roots”,或者说tracing GC的”根集合”,就是一组必须活跃的引用。下图关于可达性分析算法的图示:
gcroots
对象obj4,obj5,obj6虽然相互有关联,但是它们到 GC Roots是不可达的,所以它们将会判定为可回收的对象

要实现语义正确的tracing GC,就必须要能完整枚举出 所有的GC Roots,否则就可能会漏扫描应该存活的对象,导致GC错误回收了这些被漏扫的活对象。

哪些引用是 GC Roots

GC roots对应的引用可能包括:

  • 所有Java线程当前活跃的栈帧里指向 GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值
  • VM的一些静态数据结构里指向 GC堆里的对象的引用,例如说 Hotspot VM里的Universe里有很多这样的引用
  • (看情况)所有当前被加载的Java类
  • (看情况)Java类的引用类型静态变量
  • (看情况)Java类的运行时常量池里的引用类型常量(String或Class类型)
  • (看情况)String常量池(StringTable)里的引用

注意,是一组必须活跃的引用,不是对象

Java可达性分析算法会不会出现循环引用问题?

从前面那张图中我们就可以看出 GC Roots是单独出来的,在对象图之外,以下摘自 R大的回答

GC Root在对象图之外,是特别定义的”起点”,不可能被对象图内的对象所引用。
一个常见的误解就是 以为GC Root是一组对象,实际上GC Root通常是一组特别管理的指针,这些指针是 tracing GC 的trace 的起点,它们不是对象图里对象,对象也不可能引用到这些”外部”的指针,另外,tracing GC能够正确处理循环引用。保证每个活对象只会被访问一次就能确定其存活性,对象图里是否存在循环引用,tracing GC都能判断对象的存活与否。

这里谈到GC Roots是指针,之前说的是引用,本质它们的意思是一样的,指针也就是引用。在官方的解释是:

1
2
3
garbage collection root

A pointer into the Java object heap from outside the heap. These come up, e.g., from static fields of classes, local references in activation frames, etc.

官方的意思是由heap外部指向 heap内的对象的指针,指针另一种说法也是引用。

小结

上面的分析更多的是参考了 R大在知乎上对相应问题的解答,R大是国内JVM巨牛级人物,他的回答都是非常权威的,所以学习 JVM的知识可以多参考 R大的分析。目前本人对 JVM也是一枚渣渣级选手,现在输出对JVM的一些学习笔记。如有错误之处,欢迎指出。

参考资料 & 鸣谢

Bagikan Komentar