垃圾回收算法-标记
# 垃圾回收算法
垃圾回收主要是针对堆和方法区进行;程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收。
1
# 一. 垃圾标记阶段
概述
堆中存放这几乎所有的Java对象实例,在GC执行垃圾回收之前,首先区分内存区哪些是存活对象,哪些是已经死亡对象。被标记为已经死亡的对象,GC才会在执行垃圾回收时,释放内存空间。判断对象存活的方式: 引用计数器算法和可达性分析算法
# 1.1 引用计数算法
引用计数算法对每个对象保存一个整型的引用计数器属性,用于记录对象被引用的情况。
当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。
1
2
2
# 优点
实现简单,垃圾对象便于辨识。判定效率高。回收没有延迟性。
# 缺点
- 增加了存储空间的开销
- 无法处理循环引用的情况,致命缺陷。
# 1.2 可达性分析算法
可达性算法:也可以称为 根搜索算法、追踪性垃圾收集。
当前主流的商用程序语言(Java、C#,上溯至前面提到的古老的Lisp)的内存管理子系统,都是
通过可达性分析(Reachability Analysis)算法来判定对象是否存活的。这个算法的基本思路就是通过
一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过
程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,
或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。
1
2
3
4
5
6
2
3
4
5
6

在 Java 中 GC Roots 一般包含以下内容:
- 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的 参数、局部变量、临时变量等。
- 方法区中类静态属性引用的对象。比如Java类的引用类型静态变量。
- 方法区中的常量引用的对象。比如字符串常量池的引用。
- 本地方法栈中引用的对象。
# 二. finalization机制
对象在回收之前涉及了一个方法的调用 finalize()
- Java语言提供了对象finalization机制允许开发人员提供对象被销毁之前的自定义处理逻辑。
- 当垃圾回收器发现没有引用指向一个对象,即:垃圾回收此对象之前。总会调用这个对象的finalize()方法
- finalize()方法允许在子类中被重写,用于在对象被回收时进行资源释放。通常这个方法进行一些资源释放和清理的工作。比如关闭文件和数据库连接等。
- 永远不要主动调用对象的finalize()方法,应该交给垃圾回收机制调用。理由:
- 在finalize()时可能导致对象复活。
- finalize() 方法的执行时间是没有保障的,它会完全由GC线程决定,极端情况下若不发生GC,则finalize()方法将没有执行机会。
- 一个糟糕的finalize()方法严重影响GC的性能。
- 由于finalize()方法的存在,虚拟机中的对象一般处于三种可能的状态
- 如果从所有的根节点都无法访问到某个对象,说明对象己经不再使用了。一般来说,此对象需要被回收。但事实上,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段。一个无法触及的对象有可能在某一个条件下“复活”自己,如果这样,那么对它的回收就是不合理的,为此,定义虚拟机中的对象可能的三种状态。如下:
- 可触及的: 从根节点,可以到达这个对象。
- 可复活的: 对象的所有引用都被释放,但是对象有可能在finalize()中复活。
- 不可触及的: 对象的finalize()被调用,并且没有复活,那么就会进入不可触及状态。不可触及的对象不可能被复活,因为finalize()只会被调用一次。
- 如果从所有的根节点都无法访问到某个对象,说明对象己经不再使用了。一般来说,此对象需要被回收。但事实上,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段。一个无法触及的对象有可能在某一个条件下“复活”自己,如果这样,那么对它的回收就是不合理的,为此,定义虚拟机中的对象可能的三种状态。如下:
# 2.1 具体过程
判定一个对象objB是否可回收,至少要经历两次标记过程:
- 如果对象objB到GC Roots没有引用链,则进行第一次标记。
- 进行筛选,判断此对象是否有必要执行finalize()方法
- 如果对象objB没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机视为“没有必要执行”,objB被判定为不可触及的。
- 如果对象objB重写了finalize()方法,且还未执行过,那么objB会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize()方法执行。
- finalize()方法是对象逃脱死亡的最后机会,稍后GC会对F-Queue队列中的对象进行第二次标记。如果objB在finalize()方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,objB会被移出“即将回收”集合。之后,对象会再次出现没有引用存在的情况。在这个情况下,finalize()方法不会被再次调用,对象会直接变成不可触及的状态,也就是说,一个对象的finalize()方法只会被调用一次。
编辑 (opens new window)
上次更新: 2023/03/28, 16:13:29