JVM在进行垃圾回收之前,必然要做的就是判断哪些对象可以回收,哪些对象不可以回收,要进行这样的划分一般有两种方式,一种为引用链计数法,一种为可达性分析法。
引用计数法
这是一种非常简单的方法,为每一个对象中添加一个计数器,当对象被引用时,计数器就加1,当引用被释放时,计数器就减1,最后如果计数器为0,则表示该对象没有任何引用关系了,即为垃圾对象。
这是一种实现简单,判定效率较高的方法,也有一些著名的应用案例,比如Python语言中就使用了这种方式,但是在JVM中并没有使用这种算法,因为它存在一个明显的问题:循环引用。
如下图所示,A引用B,B引用A,除此之外再无其他任何对象引用了这个两个对象,所以这两个对象应当为垃圾对象,但却因为计数器都不为0,所以不能被回收。
可达性分析法
为了避免上述的问题,在JVM中采用的是另一种可达性分析法来判断对象是否存活,这个算法的思想就是通过判断一系列被称为GCRoots的根对象,并作为起点,根据引用关系向下查找,查找过的路径称为引用链,如果某个对象到GCRoots对象没有任何一条引用链,则判断此对象为可回收对象。
如下图所示,D、G对象与GCRoots没有任何引用链关系所以为可回收对象。
在Java技术体系里面,固定可作为GC Roots的对象包括以下几种:
1、在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的 参数、局部变量、临时变量等。
2、在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
3、在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
4、在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
5、Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
6、所有被同步锁(synchronized关键字)持有的对象。
7、反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
除此之外根据某些垃圾收集器的选用,还有可能会存在临时性的GCRoot对象,因为垃圾划代收集的方式,比如扫描新生代对象的时候,还需要考虑被老年代中对象引用的情况,此时老年代中的对象也可视为GCRoot对象。