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