• 欢迎光临~

.Net GC 结构体GCInfo剖析,

开发技术 开发技术 2022-05-29 次浏览

前言:
dotnet已经发展到了 preview 7的版本,这个GC回收的文章或者视频非常少,几乎没有看到,有个别的也是模糊不清或者三言两语,或者太过于笼统,或者作者自己都没搞明白为啥是这样。至于深入到GC里面的GCinfo结构体就更少见了。因为这个结构体里面包含了根对象所在的地址,对于垃圾回收的第一个阶段标记至关重要的一环。这篇文章来解构下这个结构体。

本文以二进制和Windbg来分析下

概念:
GCInfo实际上是一个位数组,以一个位或者多个位来标记包含的字段的意义。
如何解析这个位数组呢?


代码:

     static void Main(string[] args)
     {
         Program pm = new Program();
         GC.Collect();
         Console.ReadLine();
     }

反汇编GCInfo命令:Windbg !u -gcinfo MethodDesc
以上代码编译成exe,放在windbg里面也只能仅仅看到残缺不齐的信息,只有所谓的根对象距离Rbp寄存器的偏移位置,而且它也没有说明这些信息是如何得到的。如下所示:00007ffead724918是Program.Main的MethodDesc

0:000> !u -gcinfo 00007ffead724918
Normal JIT generated code
ConsoleApp11.Program.Main(System.String[])
ilAddr is 0000025D05E82050 pImport is 0000021EE1FFB040
Begin 00007FFEAD685EC0, size 67

F:Visual Studio ProjectConsoleApp7ConsoleApp11Program.cs @ 40:
Prolog size: 0
Security object: <none>
GS cookie: <none>
PSPSym: <none>
Generics inst context: <none>
PSP slot: <none>
GenericInst slot: <none>
Varargs: 0
Frame pointer: rbp
Wants Report Only Leaf: 0
Size of parameter area: 20
Return Kind: Scalar
Code size: 67
Untracked: +rbp+10 +rbp-8 +rbp-10 +rbp-18
00007ffe`ad685ec0 55              push    rbp
00007ffe`ad685ec1 4883ec40        sub     rsp,40h
00007ffe`ad685ec5 488d6c2440      lea     rbp,[rsp+40h]
00007ffe`ad685eca c5d857e4        vxorps  xmm4,xmm4,xmm4
00007ffe`ad685ece c5fa7f65e8      vmovdqu xmmword ptr [rbp-18h],xmm4
//此处省略部分汇编

字段
比如以下这些字段的值从何而来,代表什么意义?

Prolog size: 0
Security object: <none>
GS cookie: <none>
PSPSym: <none>
Generics inst context: <none>
PSP slot: <none>
GenericInst slot: <none>
Varargs: 0
Frame pointer: rbp
Wants Report Only Leaf: 0
Size of parameter area: 20
Return Kind: Scalar
Code size: 67

解析
要理清楚这些,必须通过CLR源码来解构。
可以参考:
https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/gcinfodecoder.cpp

一.在厘清如何结构GCInfo之前,需要知道是如何获取到GCInfo的。其实原理很简单
1.遍历所有的托管线程
2.通过托管线程找到托管线程变量Frame(也就是帧栈)
3.通过线程的帧栈找到线程对应的寄存器的值,比如Rip,Rsp,Rbp 等关键信息
4.当Rip被找到可以通过相应的API 寻找Rip所在的函数以及函数范围,也就是实例化EECodeInfo。调用函数为EECodeInfo::Init,而参数则为Rip。这一步是比较重要的。
5.当初始化EECodeInfo之后,就可以计算出,当前函数里面包含的变量信息,字段信息,函数信息等等。
6.GCInfo就是初始化EECodeInfo在此阶段被找到,但是GCInfo的初始化则是在RyuJiit里面执行。这个点要注意。
7.有了关键信息之后,就可以通过API函数RtlVirtualUnwind来回滚帧栈,循环找到需要的各种GC信息
具体的文章可以参考:
https://mp.weixin.qq.com/s?__biz=Mzg5NDYwNjU4MA==&mid=2247483946&idx=1&sn=6da6e76b7799ed818a6ff35affa27de0&chksm=c01c4fb1f76bc6a7cadc7e79f10dcb6a776b476a82e8628567ac178a82f44886e9190b706718&token=916104265&lang=zh_CN#rd
注意这里面包含了两个循环,一个是托管线程的循环,然后是每一个托管线程里面的帧栈循环。

程序员灯塔
转载请注明原文链接:.Net GC 结构体GCInfo剖析,
喜欢 (0)