前言
对一个东西的了解不应该仅仅是别人给的定义,不然除了字面上的定义外一无所获。
GC Roots
GC Roots的官方定义:
A garbage collection root is an object that is accessible from outside the heap。
借用知乎R大的定义,GC Roots是:
一组必须活跃的引用
最初我看到R大和官方的定义,第一反应是:这两人为什么说的不一致?
现在看来不是他们不一致,而是我理解不到位。
官方的定义是:在堆外可以获得到的对象,说白了就是堆外有引用的对象。
那什么是堆外没有引用的对象?说白了就是把某个引用置null。
Integer x = 123456;
x = null; //此时堆中有对象,但是栈帧中无引用。
R大的定义是:一组必须活跃的引用,先抛开活跃二字不谈,既然有引用,那么堆中必然有对应的对象。
所以两者说法一致,官方从堆的角度来定义,R大从栈的角度来定义。
GC Roots的种类
-
System Class:
Class loaded by bootstrap/system class loader. For example, everything from the rt.jar like java.util.* .
简单来说就是被最底层类加载器加载的类的元类型,也就是各个类的Class对象,也就是他们C++中对应的instanceKlass。
-
JNI
-
Thread
A started, but not stopped, thread.
运行态的线程,这是是第一点体现“活跃”的地方。
-
Busy Monitor:
Everything that has called wait() or notify() or that is synchronized. For example, by calling synchronized(Object) or by entering a synchronized method. Static method means class, non-static method means object.
这里简单来说就是作为锁的对象。
-
Java Local
Local variable. For example, input parameters or locally created objects of methods that are still in the stack of a thread.
这里简单来说就是局部变量,官方定义是:当前线程栈中的被创建的变量,这里是第二点体现“活跃”的地方。
所有GC Roots详情见:
https://help.eclipse.org/latest/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fconcepts%2Fgcroots.html&cp=37_2_3
GC Root的可视化
1.通过jps获得当前java程序进程id
2.使用jmap,配合read()阻塞,获得当前堆快照。
3.使用mat分析堆内存。
public class JUCTest {
public void function0() throws IOException {
List<Integer> arrayList = new ArrayList<>();
function1();
System.in.read(); //获得堆快照 0
}
public void function1() throws IOException {
List<Integer> linkedList = new LinkedList<>();
System.in.read(); // 获得堆快照 1
}
public static void main(String[] args) throws IOException {
new JUCTest().function0();
}
}
GC Roots总体布局
可以看出当前程序GC Roots分为四大类,上面已经总结过。
堆快照1分析
堆快照2分析
由此引发的思考
在进行方法调用时,方法调用前后GC root变化会很大,因为弹出栈帧会影响GC root的枚举。
GC root的枚举是依靠oopmap的,oopmap不可能对每条代码都更新一次,它在安全点统一更新。
对于一个栈顶方法,如果它在方法体内调用了System.gc(),这意味着:
- 在栈顶方法调用完毕弹出后,到达一个安全点。
- oopmap更新,刚刚栈顶方法里的创建的对象会从oopmap中删除。
- 利用oopmap完成根节点枚举。
- 进行可达性分析。
所以,方法调用后是一个很常见的安全点。