1
Card Table:为了记录在垃圾收集过程中对象的引用情况的,以便可以实现Concurrent GC
用在Concurrent GC第二阶段记录非垃圾收集堆对象对垃圾收集堆对象的引用。Dalvik虚拟机进行部分垃圾收集时,实际上就是只收集在Active堆上分配的对象。因此对Dalvik虚拟机来说,Card Table就是用来记录在Zygote堆上分配的对象在部收垃圾收集执行过程中对在Active堆上分配的对象的引用
两个Heap Bitmap,一个称为Live Heap Bitmap,用来记录上次GC之后,还存活的对象,另一个称为Mark Heap Bitmap,用来记录当前GC中还存活的对象。这样,上次GC后存活的但是当前GC不存活的对象,就是需要释放的对象。Davlk虚拟机使用标记-清除(Mark-Sweep)算法进行GC
在标记阶段,通过一个Mark Stack来实现递归检查被引用的对象,即在当前GC中存活的对象。有了这个Mark Stack,就可以通过循环来模拟函数递归调用。
HeapBitmap
struct HeapBitmap {
/* The bitmap data, which points to an mmap()ed area of zeroed
* anonymous memory.
*/
unsigned long *bits;
/* The size of the used memory pointed to by bits, in bytes. This
* value changes when the bitmap is shrunk.
*/
size_t bitsLen;
/* The real size of the memory pointed to by bits. This is the
* number of bytes we requested from the allocator and does not
* change.
*/
size_t allocLen;
/* The base address, which corresponds to the first bit in
* the bitmap.
*/
uintptr_t base;
/* The highest pointer value ever returned by an allocation
* from this heap. I.e., the highest address that may correspond
* to a set bit. If there are no bits set, (max < base).
*/
uintptr_t max;
};
其中最重要的就是成员变量bits指向的一个类型为unsigned long的数组,这个数组的每一个bit都用来标记一个对象是否存活。Java堆的起始地址为base,大小为maxSize,由此我们就知道,在Java堆上创建的对象的地址范围为[base, maxSize)。 但是通过C库提供的mspace_malloc来在Java堆分配内存时,得到的内存地址是以8字节对齐的。这意味着我们只需要(maxSize / 8)个bit来描述Java堆的对象。结构体HeapBitmap的成员变量bits是一个类型为unsigned long的数组,也就是说,数组中的每一个元素都可以描述sizeof(unsigned long)个对象的存活。在32位设备上,一个unsigned long占用32个bit,这意味着需要一个大小为(maxSize / 8 / 32)的unsigned long数组来描述Java堆对象的存活。如果换成字节数来描述的话,就是说我们需要一块大小为(maxSize / 8 / 32) × 4的内存块来描述一个大小为maxSize的Java堆对象。
2
struct GcMarkStack {
/* Highest address (exclusive)
*/
const Object **limit;
/* Current top of the stack (exclusive)
*/
const Object **top;
/* Lowest address (inclusive)
*/
const Object **base;
/* Maximum stack size, in bytes.
*/
size_t length;
};
我们需要一块大小为(maximumSize * sizeof(Object
) / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD))的内存块来描述一个GcMarkStack栈。
3
在Dalvik虚拟机中,Card Table和Heap Bitmap的作用是类似的。区别在于:
- Card Table不是使用一个bit来描述一个对象,而是用一个byte来描述GC_CARD_SIZE个对象;
- Card Table不是用来描述对象的存活,而是用来描述在Concurrent GC的过程中被修改的对象,这些对象需要进行特殊处理。
全局变量gDvm的成员变量gcHeap指向了一个GcHeap结构体。在GcHeap结构体,通过cardTableBase、cardTableLength、cardTableMaxLength和cardTableOffset这四个成员变量来描述一个Card Table。它们的定义如下所示:
struct GcHeap {
......
/* GC's card table */
u1* cardTableBase;
size_t cardTableLength;
size_t cardTableMaxLength;
size_t cardTableOffset;
......
};