内容摘抄自《深入理解Java虚拟机 第三版》
3.1 简介
略
3.2 对象已死?
在堆中存放着Java几乎所有的对象的实例,垃圾收集器在对堆进行回收之前首先判断那些对象还活着,那些对象已经死去(死去意旨不被任何途径使用的对象)
3.2.1 引用计数法
很多书籍上判断对象是否存活的算法是这样的:在对象中添加一个引用计数器,每当有一个地方引用它,计数器的值就加1;当引用失效时,计数器就减1;任何计数器为零的对象就是不可能再被使用的。
客观的来说,引用计数法(Reference Counting)虽然占用额外的额外的内存空间进行计数,但它原理简单,效率比较高,大多数情况下是不错的选择。但是在Java主流虚拟机中都没有选用引用计数法来管理内存,主要原因是这个看似简单的算法有很多例外情况要考虑,必须配合大量额外操作才能正确工作,比如单纯的引用计数法就很难解决对象之间的循环引用。
3.2.2 可达性分析
当前主流的商用系统(Java,C#等)的内存管理子系统,都是通过可达性分析(Reachability Analysis)算法来判定对象是否存活的。这个算法的基本思路就是通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链,或者从图论来说的话就是从GC Roots到这个对象不可达时,则证明对象是不可能被使用的。
下图3-1所示,对象object5,object6,object7虽然互有关联,但是他们到GC Roots是不可达的,因此他们为可回收对象。
在Java技术体系中,固定可做GC Roots的对象包括一下几种:
- 在虚拟机栈(栈帧中的局部变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数,局部变量,临时变量等
- 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量
- 在方法区中常量引用对的对象,譬如字符串常量池(String Table)里的引用
- 在本地方法JNI(几通常所说的Native方法)引用的对象
- Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(NullPointException,OutOfMemoryError)等,还有系统的类加载器
- 所有被同步锁(synchronized关键字)持有的对象
- 反映Java虚拟机内部情况的JMXBean,JVMTI中注册的回调,本地代码缓存等
除了这些固定的GC Roots集合外,根据用户所选的垃圾收集器及当前回收的内存区域不同,还可以有其他对象临时加入,共同构成完整的GC Roots集合。
目前最新的几款垃圾收集器无一例外都具有局部回收的特征。为了避免GC Roots包含过多对象而过度膨胀,他们在实现上也做了各种优化处理。
2021-09-10