Java内存管理(三)——卡片表

来源:互联网 发布:系统服务 软件更新 编辑:程序博客网 时间:2024/04/30 05:47

在垃圾回收的过程中,会碰到一个问题,就是老年代中的对象可能引用年轻代中的对象。在这种情况下,每次遍历老年代的对象来查找所有存活对象的时候就会消耗相当的时间。而且,在应用中,通常来说,这种引用是非常少的(某些研究表明,这种情况的引用占总引用量的1%都不到)。但是这些引用需要遍历整个老年代对象是一个相当大的Overhead。

Card-table

JVM中会维护一个卡片表的数据结构,主要用于查看老年代中指向年轻代中的对象。卡片表其实就是一个bit数组,每一位表明一定范围的内存是否被引用了(存在老年代指向年轻代的引用)。我们可以通过1位来表示一个4KB的内存区域。

这里写图片描述

如上图,其中√表示其中的卡片表的一位为1,表示对应的内存块是存在老年代指向年轻代对象的。

引用赋值操作

当在用户代码中执行了引用赋值操作的时候,其实都有JVM引式的将赋值操作进行了额外的处理的(JIT执行)。这部分额外处理会比较对象被赋值的地址,是否在老年代范围。如果是老年代的范围内,则将对应的card-table中的bit置为1,表示当前bit对应的内存块,现在存在老年代指向年轻代的内存,如图所示红色区域。

首先,标记的时候,只会访问到年轻代的对象。一旦标记阶段结束,会访问card-table来定位老年代所在的内存块。当找到老年代的内存块后,会将其中的所有对象当成root节点,继续执行标记。

由此可见,card-table的每个4KB的内存块,只是针对老年代的标记的一定程度上的优化。如果我们能够将粒度细化到对象的程度上,那自然就可以更大程度的减少标记的时间,但是同时,card-table的大小也将显著变大。

而且更大的card-table也会令赋值引用操作(JIT执行的额外动作来赋值card-table)变得更慢。

原创粉丝点击