写在前面

本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构和文献引用请见100个问题搞定Java虚拟机

解答

引用计数算法

给对象中关联一个引用计数器,当有一个地方引用它时,计数器数值就加1;当引用失效时,计数器数值就减1;任意时刻计数器数值为0就代表当前对象已死。

每个计数器只记录了其对应对象的局部信息——被引用的次数,而没有(也不需要)一份全局的对象图的生死信息。

由于只维护局部信息,所以不需要扫描全局对象图就可以识别并释放死对象;

但也因为缺乏全局对象图信息,所以无法处理循环引用的状况。

高级的引用计数实现会引入“弱引用”的概念来打破某些已知的循环引用。

优点

实现简单,判定效率高

缺点

  1. 需要额外的空间来存储计数器,以及繁琐的更新操作。
  2. 很难解决对象之间循环引用的问题。

典型应用

python

可达性分析算法

先找出一批根节点对象集合作为GC Roots(可称为根节点枚举),然后从这批根节点出发,查找其引用关系(类似于深度优先搜索),最终形成一个反映对象间依赖关系的图。

若某些对象没有任何引用链与GC Roots相连(从图论角度来说,代表从GC Roots到当前对象不可达),则说明这些对象就是垃圾对象,是可以被垃圾收集器回收的。

优点

很容易解决对象之间循环引用的问题。

缺点

在多线程场景下,遍历 GC Roots 的过程可能会有新的对象产生,对象之间引用也可能发生变更。

为了避免误报(将对象设置为 null)和漏报(将对象设置为不可达对象),必须在枚举 GC Roots 的时候进行全局暂停,这也是 Java 中 stop the world 的根本原因之一。

典型应用

Java

补充

GC Roots包括哪些?

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  2. 方法区中类静态属性引用的对象。
  3. 方法区中常量引用的对象。
  4. JNI handles。
  5. 已启动且未停止的 Java 线程。
上一篇 下一篇