垃圾标记阶段:主要是为了判断对象是否存活
-
在堆里存放着几乎所有的 Java 对象实例,在 GC 执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为己经死亡的对象,GC 才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段。
-
那么在 JVM 中究竟是如何标记一个死亡对象呢?
简单来说,当一个对象已经不再被任何的存活对象继续引用时,就可以宣判为已经死亡。
-
判断对象存活一般有两种方式:引用计数算法和可达性分析算法。
引用计数算法
引用计数算法(Reference Counting)比较简单,对每个对象保存一个整型 的引用计数器属性。用于记录对象被引用的情况
对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加 1; 当引用失效时,引用计数器就减 1。只要对象 A 的引用计数器的值为 0,即表示 对象 A 不可能再被使用,可进行回收
优点
实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性
缺点
1.它需要单独的字段存储计数器,这样的做法增加了存储空间的开销。
2.每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销。
3.引用计数器有一个严重的问题,即无法处理循环引用的情况。这是一条致命缺陷,导致在.Java 的垃圾回收器中没有使用这类算法。
可达性分析算法
可达性分析算法,也称为根搜索算法、追踪性垃圾收集
1.相对于引用计数算法而言,可达性分析算法不仅同样具备实现简单和执行高效等特点,更重要的是该算法可以有效地解决在引用计数算法中循环引用的问题, 防止内存泄漏的发生。
2.相较于引用计数算法,这里的可达性分析就是 Java、C#选择的。这种类型的垃圾收集通常也叫作追踪性垃圾收集(Tracing Garbage Collection)
实现思路
1.可达性分析算法是以根对象集合(GCRoots)根集合就是一组必须活跃的引用为起始点,按照从上至下的方式 搜索被根对象集合所连接的目标对象是否可达。
2.使用可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接 着,搜索所走过的路径称为引用链(Reference Chain)
3.如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象己经死亡, 可以标记为垃圾对象。
4.在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象
GC Roots 可以是哪些元素
1.虚拟机栈中引用的对象
比如:各个线程被调用的方法中使用到的参数、局部变量等。
2.本地方法栈内 JNI(通常说的本地方法)引用的对象
3.方法区中类静态属性引用的对象
比如:Java 类的引用类型静态变量
4.方法区中常量引用的对象
比如:字符串常量池(StringTable)里的引用
5.所有被同步锁 synchronized 持有的对象
6.Java 虚拟机内部的引用。 基 本 数 据 类 型 对 应 的 Class 对 象 , 一 些 常 驻 的 异 常 对 象 ( 如 : NullPointerException、OutofMemoryError),系统类加载器