写在前面
本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构和文献引用请见100个问题搞定Java虚拟机
解答
引用计数算法
给对象中关联一个引用计数器,当有一个地方引用它时,计数器数值就加1;当引用失效时,计数器数值就减1;任意时刻计数器数值为0就代表当前对象已死。
每个计数器只记录了其对应对象的局部信息——被引用的次数,而没有(也不需要)一份全局的对象图的生死信息。
由于只维护局部信息,所以不需要扫描全局对象图就可以识别并释放死对象;
但也因为缺乏全局对象图信息,所以无法处理循环引用的状况。
高级的引用计数实现会引入“弱引用”的概念来打破某些已知的循环引用。
优点
实现简单,判定效率高
缺点
- 需要额外的空间来存储计数器,以及繁琐的更新操作。
- 很难解决对象之间循环引用的问题。
典型应用
python
可达性分析算法
先找出一批根节点对象集合作为GC Roots(可称为根节点枚举),然后从这批根节点出发,查找其引用关系(类似于深度优先搜索),最终形成一个反映对象间依赖关系的图。
若某些对象没有任何引用链与GC Roots相连(从图论角度来说,代表从GC Roots到当前对象不可达),则说明这些对象就是垃圾对象,是可以被垃圾收集器回收的。
优点
很容易解决对象之间循环引用的问题。
缺点
在多线程场景下,遍历 GC Roots 的过程可能会有新的对象产生,对象之间引用也可能发生变更。
为了避免误报(将对象设置为 null)和漏报(将对象设置为不可达对象),必须在枚举 GC Roots 的时候进行全局暂停,这也是 Java 中 stop the world 的根本原因之一。
典型应用
Java
补充
GC Roots包括哪些?
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- JNI handles。
- 已启动且未停止的 Java 线程。