垃圾判断
垃圾介绍
垃圾:如果一个或多个对象没有任何的引用指向它了,那么这个对象现在就是垃圾
作用:释放没用的对象,清除内存里的记录碎片,碎片整理将所占用的堆内存移到堆的一端,以便 JVM 将整理出的内存分配给新的对象
垃圾收集主要是针对堆和方法区进行,程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收
在堆里存放着几乎所有的 Java 对象实例,在 GC 执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为己经死亡的对象,GC 才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程可以称为垃圾标记阶段,判断对象存活一般有两种方式:引用计数算法和可达性分析算法
引用计数法
引用计数算法(Reference Counting):对每个对象保存一个整型的引用计数器属性,用于记录对象被引用的情况。对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加 1;当引用失效时,引用计数器就减 1;当对象 A 的引用计数器的值为 0,即表示对象A不可能再被使用,可进行回收(Java 没有采用)
优点:
- 回收没有延迟性,无需等到内存不够的时候才开始回收,运行时根据对象计数器是否为 0,可以直接回收
- 在垃圾回收过程中,应用无需挂起;如果申请内存时,内存不足,则立刻报 OOM 错误
- 区域性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象
缺点:
每次对象被引用时,都需要去更新计数器,有一点时间开销
浪费 CPU 资源,即使内存够用,仍然在运行时进行计数器的统计。
无法解决循环引用问题,会引发内存泄露(最大的缺点)
1
2
3
4
5
6
7
8
9
10
11public class Test {
public Object instance = null;
public static void main(String[] args) {
Test a = new Test();// a = 1
Test b = new Test();// b = 1
a.instance = b; // b = 2
b.instance = a; // a = 2
a = null; // a = 1
b = null; // b = 1
}
}
可达性分析
GC Roots
可达性分析算法:也可以称为根搜索算法、追踪性垃圾收集
什么是对象可达?
对象可达指的就是:双方存在直接或间接的引用关系。
根可达或GC Roots可达就是指:对象到GC Roots存在直接或间接的引用关系。
GC Roots 对象:
- 虚拟机栈中局部变量表中引用的对象:各个线程被调用的方法中使用到的参数、局部变量等
- 本地方法栈中引用的对象
- 堆中类静态属性引用的对象
- 方法区中的常量引用的对象
- 字符串常量池(string Table)里的引用
- 同步锁 synchronized 持有的对象
GC Roots 是一组活跃的引用,不是对象,而且是JVM确定当前绝对不能被回收的对象的引用,放在 GC Roots Set 集合
当JVM触发GC时,首先会让所有的用户线程到达安全点SafePoint时阻塞,也就是STW,然后枚举根节点,即找到所有的GC Roots,然后就可以从这些GC Roots向下搜寻,可达的对象就保留,不可达的对象就回收。
即使是号称几乎不停顿的CMS、G1等收集器,在枚举根节点时,也是要暂停用户线程的。
下面就一起来理解一下为什么这几类对象可以被作为GC Roots。
1、方法区静态属性引用的对象
全局对象的一种,Class对象本身很难被回收,回收的条件非常苛刻,只要Class对象不被回收,静态成员就不能被回收。
2、方法区常量池引用的对象
也属于全局对象,例如字符串常量池,常量本身初始化后不会再改变,因此作为GC Roots也是合理的。
3、方法栈中栈帧本地变量表引用的对象
属于执行上下文中的对象,线程在执行方法时,会将方法打包成一个栈帧入栈执行,方法里用到的局部变量会存放到栈帧的本地变量表中。只要方法还在运行,还没出栈,就意味这本地变量表的对象还会被访问,GC就不应该回收,所以这一类对象也可作为GC Roots。
4、JNI本地方法栈中引用的对象
和上一条本质相同,无非是一个是Java方法栈中的变量引用,一个是native方法(C、C++)方法栈中的变量引用。
5、被同步锁持有的对象
被synchronized锁住的对象也是绝对不能回收的,当前有线程持有对象锁呢,GC如果回收了对象,锁不就失效了嘛。
工作原理
可达性分析算法以根对象集合(GCRoots)为起始点,从上至下的方式搜索被根对象集合所连接的目标对象
分析工作必须在一个保障一致性的快照中进行,否则结果的准确性无法保证,这也是导致 GC 进行时必须 Stop The World 的一个原因
基本原理:
可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索走过的路径称为引用链
如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象己经死亡,可以标记为垃圾对象
在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象