JVM中解决MinorGC扫描全堆的方法
极客时间郑雨迪老师的《深入拆解Java虚拟机》的解释
MinorGC的是当新生代不够时触发的GC算法,它的好处就是不用堆整个堆进行垃圾回收或者扫描,但是如果当老年代的对象引用到新生代的对象,在判断对象是否存活时,采用了GC Roots可达性分析,需要标记存活对象,照这个道理,JVM还是需要扫描老年代,也就是扫描整个堆了。这不前后矛盾了嘛。那么JVM是怎么做的呢?
JVM内部维持了一个卡表(Card Table)技术,将整个堆分为大小512字节的若干卡,并且通过一个卡表记录每个卡的标识位。这个标识位代表的是该卡中是否存在有指向新生代对象的引用,存在就是脏表。
所以在发生MinorGC时,为了判断对象是否存活,就不需要去遍历整个堆,而是去遍历卡表,找到藏卡,并将藏卡中的对象加入到Minor GC的GC Roots中。完成脏卡扫描之后,JVM还会将脏卡标志位清零。
由于 Minor GC 伴随着存活对象的复制,而复制需要更新指向该对象的引用。因此,在更新引用的同时,我们又会设置引用所在的卡的标识位。这个时候,我们可以确保脏卡中必定包含指向新生代对象的引用。
在 HotSpot 中,卡表是通过 byte 数组来实现的。对于一个 64 字节的缓存行来说,如果用它来加载部分卡表,那么它将对应 64 张卡,也就是 32KB 的内存。