GC描述 java中的GC在帮助我们进行垃圾回收时,会STW(Stop the World)。 先说一下GC是如何快速枚举根节点。在HotSpot虚拟机中,是通过可达性分析来判断此对象是否需要回收的。那可达性分析就需要找到“源头”,也就是根节点。
通过枚举每一个根节点(GC Roots),然后顺着每个节点遍历下来,之后没有被遍历到的那些对象就会被回收了。那这个顺藤摸瓜的过程就得本机器中的所有进程停止运行,也就是所有的工作线程都得停了。
你想如果不STW那对象引用关系变来变去的,垃圾收集器得怎么咔嚓对象啊,容易咔嚓错了,那咱们使用者不就急眼了啊。所以枚举根节点时STW不可避免,所以只能让STW尽量的短。
根节点主要在全局性的引用(常量、类静态属性)和执行上下文(栈帧中的本地变量表)中。那我们如果要一个一个的找过去就很慢。
并且我们的HotSpot又是准确性GC,也就是它需要知道某个位置上的某个数据的类型,类型是准确的。这样它就能准确的知道这块数据类型是不是它关心的指针也就是引用啦!
在HotSpot中是用了一种叫OopMap的结构来存放一个对象内什么偏移量上是什么类型的数据。在类加载过程中就会进行记录。
可以把OopMap理解为一个附加信息,或者说一件衣服的吊牌,咱们看吊牌就知道这衣服啥做的。所以GC在扫描的时候就可以直接看这些“吊牌”来知道信息了。
在JIT编译的时候也会在一些特定的位置记录下OopMap,记录了执行到该方法的某条指令的时候,栈上和寄存器里哪些位置是引用,每个方法可能会有好多个OopMap,这是根据特定位置来决定的,这个特定位置会把这个方法分会好几块,每一块都有一个OopMap。
这些特定的位置主要在: 1、方法临返回前/调用方法的call指令后 2、循环的末尾 3、可能抛出异常的地方 这些特定的位置也叫安全点(Safepoint)。
之所要在特定的位置才记录OopMap,是因为如果对每条指令都记录一下的话,那就会需要大量的空间,提高了GC的空间成本,所以用一些比较关键的点来记录就能有效的缩小记录所需的空间。 因此GC不是随时随地来的,得到达安全点时才可以开始GC。 平时OopMap是压缩在内存中,只要当要GC的时候才会解压出来,然后开始遍历来扫描对应的偏移量。 对于JNI(Java Native Interface)方法,因为本地方法和解释器、JIT编译器没啥关系,所以就没有OopMap。 它的引用是加了个中间层一样的,就是句柄,也就是引用不是直接指向堆中的对象,而是引用指向句柄,句柄指向堆中对象。所以GC直接扫描句柄就行了,不需要扫描栈帧。