roots攻略 搜寻 锈湖攻略
垃圾收集(Garbage Collection,下文简称GC)是Java与其它编程语言不同,GC主要考虑三个问题:
今天我们主要谈谈JVM如何判断对象可以回收。
常用的算法有两种:
在对象中添加一个引用计数器。每次添加一个引用,计数器将添加1。当引用失效时,计数器将减少1。任何时候,只要计数器为0,就意味着对象可以回收而不引用。
该算法实现简单,判断效率高,但存在一些缺点:
主流的商用JVM没有这样实现。
可达性分析算法主流商业用途JVM通过可达性分析来判断对象是否可以回收。
该算法的基本思路是:
通过一系列被称为「GC Roots」作为起始节点集,从这些节点开始,通过引用关系向下搜索,搜索路径称为「引用链」,若对象到达GC Roots没有引用链连接,说明对象无法到达,可以回收。
第一次看这段话是不是一脸懵?一开始作者也是,完全不知道是什么意思,后来才慢慢明白。
要理解可达性算法,首先要理解几个问题:
对象可达是指双方之间存在直接或间接的引用关系。
根可达或GC Roots可达是指对象到达GC Roots存在直接或间接的引用关系。
如下代码:
public class MyObject {private String objectName;//对象名private MyObject refrence;//依赖对象public MyObject(String objectName) {this.objectName = objectName;}public MyObject(String objectName, MyObject refrence) {this.objectName = objectName;this.refrence = refrence;}public static void main(String[] args) {MyObject a = new MyObject("a");MyObject b = new MyObject("b");MyObject c = new MyObject("c");a.refrence = b;b.refrence = c;new MyObject("d", new MyObject("e")
他们之间的引用关系如图所示:
假设a是GC Roots的话,那么b、c是可达的,d、e不可达。
垃圾回收时,JVM首先要找到一切GC Roots,这个过程叫做 「枚举根节点」 ,这个过程需要暂停用户线程,即触发STW。
然后再从GC Roots向下搜索这些根节点,保留可达对象,回收不可达对象。
那到底是什么呢?GC Roots呢?
GC Roots是对象,是JVM确定目前不能回收的对象(如方法区中类静态属性引用的对象) )。
只有找到这种对象,后面的搜寻过程才有意义,不能被回收的对象所依赖的其他对象肯定也不能回收嘛。
当JVM触发GC首先,所有用户线程都将达到安全点SafePoint时间阻塞,即STW,然后枚举根节点,找到所有GC Roots,然后就可以这样了GC Roots向下搜索,保留可达对象,回收不可达对象。
即使声称几乎不停顿,CMS、G在枚举根节点时,1等收集器也应暂停用户线程。
GC Roots它是一个特殊的对象,也是一个特殊的对象Java在运行过程中,程序必须是根对象。
那么,哪些对象可以成为呢?GC Roots呢?
可以作为GC Roots对象可分为两类:全局对象和执行上下文。
让我们来看看为什么这些对象可以被视为GC Roots。
1.引用方法区静态属性的对象
一种全局对象,Class只要对象本身难以回收,回收条件就非常苛刻Class对象不回收,静态成员不回收。
2.方法区常量池引用的对象
也属于全局对象,如字符串常量池,常量本身初始化后不会改变,所以作为GC Roots也是合理的。
3.方法栈中栈帧本地变量表引用的对象
属于执行上下文中的对象。当线程执行方法时,该方法将法包装成堆栈帧,并将该方法中使用的局部变量存储在堆栈帧的本地变量表中。只要该方法仍在运行中,并且没有离开堆栈,这意味着本地变量表的对象将被访问,GC不应该回收,所以这种对象也可以作为GC Roots。
4、JNI本地方法栈引用的对象
和上一个一样,无非是一个Java方法栈中的变量引用是native方法(C、C )方法栈中的变量引用。
5.被同步锁定的对象
被synchronized锁定对象绝对不能回收,目前有线程持有对象锁,GC如果对象被回收,锁不会失效。
综上所述,可达性分析是JVM首先,列出根节点,找到一些必须存活的对象,以确保程序正常运行,然后根据参考关系开始向下搜索,直接或间接参考链存活,没有参考链回收。