lldb watch card_table (CLR) 的运行模式
最编程
2024-03-01 22:28:00
...
1.前言 前面两篇(CLR跨代标记内存模型,CLR card_table位移和数组)我们看了下跨代引用的理论知识,本篇来验证下这些说法。
2.概述 示例
public class Name {
private string first;
public Name selfName;
public Name(string first){
this.first = first;
}
}
static void Main(string[] args){
Name n1 = new Name("1111");
GC.Collect();GC.Collect();
Name n2 = new Name("3333");
n1.selfName= n2;C.Collect(0);
}
老年代的n1.selfName引用了新生代(短暂堆)的n2。
3.观察 命令如下:
b RunMainInternal
c
b PreStubWorker
c
b prestub.cpp:1967
c
br del
n
n
s
此时来到了托管Main入口
(lldb) s
Process 5442 stopped
* thread #1, name = 'clrrun', stop reason = instruction step into
frame #0: 0x00007fff78d85470
-> 0x7fff78d85470: push rbp
0x7fff78d85471: sub rsp, 0x30
0x7fff78d85475: lea rbp, [rsp + 0x30]
0x7fff78d8547a: xor eax, eax
看下它的栈
(lldb) di -s $pc -c 0x30
-> 0x7fff78d85470: push rbp
0x7fff78d85471: sub rsp, 0x30
0x7fff78d85475: lea rbp, [rsp + 0x30]
//中间省略
0x7fff78d85511: lea rdi, [rdi + 0x10]
0x7fff78d85515: mov rsi, qword ptr [rbp - 0x18]
0x7fff78d85519: call 0x7ffff730f6e0 ; JIT_WriteBarrier
在0x7fff78d85519处断
b 0x7fff78d85519
c
可以看到JIT_WriteBarrier,此时n1.selfName==rdi And n2==rsi
(lldb) register read rdi rsi
rdi = 0x00007fbf6a808b08
rsi = 0x00007fbf6cc00028
此时的n2的MT(MethodTable)尚未标记存活
(lldb) x/8gx $rsi
0x7fbf6cc00028: 0x00007fff7911c470 0x00007fffe6bff8e0
0x7fbf6cc00038: 0x0000000000000000 0x0000000000000000
0x7fbf6cc00048: 0x0000000000000000 0x0000000000000000
0x7fbf6cc00058: 0x0000000000000000 0x0000000000000000
继续,此处在mark_through_cards_for_segments
(lldb) b gc.cpp:38448
Breakpoint 21: 2 locations.
(lldb) c
limit = min (end, card_address (end_card));
注意看这个limit它就是老年代循环遍历,找出老年代引用的新生代对象,然后对新生代对象进行标记
(lldb) p/x limit
(uint8_t *) $16 = 0x00007fbf6a808b18 ""
limit是遍历循环老年代的结束范围,而n1.selfName的地址是0x00007fbf6a808b08,刚好被包括进来,此时就可以查找到n1.selfName引用的对象,对其进行标记。
此时注意了。请看下面n2的MT
(lldb) x/8gx 0x00007fbf6cc00028
0x7fbf6cc00028: 0x00007fff7911c471 0x00007fffe6bff8e0
0x7fbf6cc00038: 0x0000000000000000 0x0000000000000000
0x7fbf6cc00048: 0x0000000000000000 0x0000000000000000
0x7fbf6cc00058: 0x0000000000000000 0x0000000000000000
它已经被标记存活了,怎么回事?因为n2是根对象,所以在mark_phase的时候已经被标记,此处再次对它进行标记,是因为如果n2不是根对象,mark_phase可能未必标记,此处是为了避免漏掉标记。
4.拾遗 上面似乎没有card_table的操作和内存模型,可以粗略的看下card_table
b find_card
c
Process 5442 stopped
* thread #1, name = 'clrrun', stop reason = step over
frame #0: 0x00007ffff72f809c libcoreclr.so`WKS::gc_heap::find_card(card_table=0x00007faedb5ff040, card=0x00007fffffffba48, card_word_end=17146007553, end_card=0x00007fffffffbab0) at gc.cpp:37953:30
37950
37951 // Find the first card which is set
37952 last_card_word = &card_table [card_word (card)];
-> 37953 bit_position = card_bit (card);
它里面跟之前描述基本无差。另外的内存模型可以通过set_card推导,Linux和windows似乎不太一样,还需要研究。