1、垃圾标记阶段
- 对象存活判断:在堆中存放着几乎所有的java对象实例,在GC执行垃圾回收之前,首先需要区分内存中那些是存活对象,哪些是已经死亡的对象,只有被标记为死亡的对象,GC才会在执行垃圾回收的时候,释放其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段
- 那么JVM中究竟如何标记一个死亡对象呢? 简单来说,当一个对象已经被不在被任何存活的对象继续引用,就可以宣判已经死亡
- 判断对象存活一般有两种方式:引用技术算法和可达性分析算法
1、2 引用计数算法(Java没有采用)
- 引用计数算法(Reference Counting) 比较简单,对每个对象保存一个整形的应用计数器属性,用于记录对象的被引用情况
- 对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1;当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,即表示对象A不可能再被使用,可进行回收。
- 优点:实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性。
- 缺点:
- ➢它需要单独的字段存储计数器,这样的做法增加了存储空间的开销。
- ➢每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销。
- ➢引用计数器有一个严重的问题,即无法处理循环引用的情况。这是一 条致命缺陷,导致在Java的垃圾回收器中没有使用这类算法。
循环引用导致的内存泄露问题
证明:Java使用的不是引用技术算法
/**
* -XX:+PrintGCDetails
* 证明:java使用的不是引用计数算法
*/
public class RefCountGC {
//这个成员属性唯一的作用就是占用一点内存
private byte[] bigSize = new byte[5 * 1024 * 1024];//5MB,仅仅意味着只要创建我这个RefCountGC对象实例,就会占用5M的堆空间
Object reference = null;
public static void main(String[] args) {
RefCountGC obj1 = new RefCountGC();
RefCountGC obj2 = new RefCountGC();
obj1.reference = obj2;
obj2.reference = obj1;
obj1 = null;
obj2 = null;
//显式的执行垃圾回收行为
//这里发生GC,obj1和obj2能否被回收?能,证明Java没有采用引用计数算法
System.gc();
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
引用技术算法的弊端:即使把obj1 一reference和obj2 一reference(栈引用)置null。 则在Java堆当中的两块内存依然保持着互相引用,无法回收。
但是把obj1 一reference和obj2 一reference置为null之后,如果显示的去调用System.gc();这里将会发生GC,回收堆中bj1和obj2实体。从而证明Jlava没有采用引用计数算法 .
1、2 可达性分析算法
也叫根搜索算法或追踪性垃圾收集
- 相对于引用计数算法而言,可达性分析算法不仅同样具备简单和执行高效率等特点,更重要的是该算法可以有效的解决在引用计数算法中循环引用的问题,防止内存泄露的发生
- 相较于引用计数算法,这里的可达性分析就是java、c#选择的。种类型的垃圾收集通常也叫作追踪性垃圾收集(Tracing GarbageCollection)。
- 所谓"GC Roots"根集合就是一组必须活跃的引用。
- ➢可达性分析算法是以根对象集合(GCRoots)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。
- ➢使用可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索所走过的路径称为引用链(Reference Chain)
1、3 GC Roots
在java语言中,GC Roots对象包括以下几类元素
1、虚拟机栈中引用的对象:➢比如:各个线程当中被调用的方法中使用到的参数、局部变量等。
2、.本地方法栈内JNI(通常说的本地方法)引用的对象
3、方法区中类静态属性引用的对象➢比如:Java类的引用类型静态变量
Class Dog {
private static Object tail;
}
4、方法区中常量引用的对象:➢比如:字符串常量池(string Table) 里的引用
Class Dog {
private final Object tail;
}
5、所有被同步锁synchronized持有的对象
6、Java虚拟机内部的引用:➢基本数据类型对应的Class对象,一些常驻的异常对象(如: NullPointerException、OutOfMemoryError) ,系统类加载器。、
7.反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等.
8、除了这些固定的GCRoots集合以外,根据用户所选用的垃圾收集器以及当 前回收的内存区域不同,还可以有其他对象“临时性”地加入,共同构成完整GC Roots集合。比如:分代收集和局部回收(Partial GC)。
➢如果只针对Java堆中的某一块区域进行垃圾回收(比如:典型的只针 对新生代),必须考虑到内存区域是虚拟机自己的实现细节,更不是孤立封闭的