1.前言 计算里面的0和1是基础中的基础,所以细节非常重要。本篇继续研看下card_table的一些细节。
2.细节一:数组位移 在进行数组操作的时候,根据数组的类型对数组索引进行位移。如下代码:
typedef unsigned int uint32_t;
int main(int argc,char** argv) {
uint32_t card_table[] = { 1,3,5,7,9,11,13,15,17 };
uint32_t* g_card_table = card_table;
uint32_t bone = g_card_table[2];
}
g_card_table[2]的机器码层面实际上是这样:
00007FF606F05F7B 48 8D 45 08 lea rax,[card_table]
00007FF606F05F7F 48 89 45 48 mov qword ptr [g_card_table],rax
00007FF606F05F83 B8 04 00 00 00 mov eax,4
00007FF606F05F88 48 6B C0 02 imul rax,rax,2
00007FF606F05F8C 48 8B 4D 48 mov rcx,qword ptr [g_card_table]
00007FF606F05F90 8B 04 01 mov eax,dword ptr [rcx+rax]
00007FF606F05F93 89 45 64 mov dword ptr [bone],eax
以上代码在Linux下面可能是这样:
Windows:00007FF606F05F88 48 6B C0 02 imul rax,rax,2
Linux :00007FF606F05F88 48 6B C0 02 shr rax,2
Windows:
00007FF606F05F8C 48 8B 4D 48 mov rcx,qword ptr [g_card_table]
00007FF606F05F90 8B 04 01 mov eax,dword ptr [rcx+rax]
Linux:
add eax,[g_card_table+rax]
简单点来说就是:
因为int32_t占四字节,所以
(g_card_table+索引<<2)==bone
也就是说数组里面的索引需要左移,这里应用到card_table,比如:
n1.selfName的地址是:0x00007fbf6a808b08
如果想要取它在card_table里面的0x00007fbf6a808b08>>0xB处索引
就需要把n1.selfName>>8>>5作为card_table索引,也即是:
card_table[n1.selfName>>8>>5];
>>8>>5和0xB相比,多右移了两位,因为数组是int它会自动左移两位。
那么JIT_WriteBarrier_Debug(Debug版本)是如何操作的呢?只需要看下面这部分代码即可
0x7ffff730f72e <+78>: movabs rax, 0x7faedb5ff040
0x7ffff730f738 <+88>: mov ecx, r8d
0x7ffff730f73b <+91>: shr r8, 0xb
0x7ffff730f73f <+95>: shr ecx, 0x8
0x7ffff730f742 <+98>: and ecx, 0x7
0x7ffff730f745 <+101>: mov dl, 0x1
0x7ffff730f747 <+103>: shl dl, cl
0x7ffff730f749 <+105>: test byte ptr [r8 + rax], dl
0x7ffff730f74d <+109>: je 0x7ffff730f751 ; <+113>
0x7ffff730f74f <+111>: rep ret
0x7ffff730f751 <+113>: lock
0x7ffff730f752 <+114>: or byte ptr [r8 + rax], dl
意思是:
(card_table(rax)+n1.selfName>>0xB) || 1<<3
取值呢?find_card里面
last_card_word = &card_table [card_word (card)];
card的值是n1.selfName/256(2的八次方),card_word是在card基础上再除以32(2的五次方),结果就是除以0xD,因为是int数组操作所以需左移2位,card_table的索引即是n1.selfName/0xB.跟上面JIT_WriteBarrier_Debug符合。
3.细节二:card_table范围查找 linux下面一个card覆盖了2的8次方也即是256个字节的范围,一个card_word则覆盖了2的13次方,也即是8192个字节范围。 只要n1.selfName的值在这个范围内即可。在find_card里面find_card_dword找到最后的范围
BOOL gc_heap::find_card(uint32_t* card_table,
size_t& card,
size_t card_word_end,
size_t& end_card)
{
size_t lcw = card_word(card) + (bit_position != 0);
if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
{
return FALSE;
}
else
{
last_card_word = &card_table [lcw];
card_word_value = *last_card_word;
}
bit_position = 0;
}
这个计算出来的结果last_card_word,覆盖的8192个字节范围包含了n1.selfName对象的地址,那么mark_through_cards_for_segments里面的变量o和limit循环这个范围,找出n1.selfName引用的n2,对其进行标记。
4.细节三:CARD_BUNDLE 这个标志,应该是在8192位一个单位,再次进行标记。以缩小范围的查找。