.Net8字符串初始化和全局变量内存模型

本文详细探讨了.NET8中字符串何时初始化,特别关注静态变量的内存模型,通过实例分析了全局字符串表的创建过程以及JIT编译时如何获取字符串信息。还讨论了静态列表内存追踪的方法。
摘要由CSDN通过智能技术生成

1.前言
.Net8里面的字符串何时被初始化的,全局变量内存模型怎么样的呢?本篇看下

2.概述
一:例子
先上例子

static void Main(string[] args)
{
   string str = "abcdefg";
}

看下它的汇编

string str = "abcdefg";
00007FFE40B506DB 48 B9 68 20 40 1A D6 01 00 00 mov         rcx,1D61A402068h  
00007FFE40B506E5 48 8B 09             mov         rcx,qword ptr [rcx]  
00007FFE40B506E8 48 89 4D 38          mov         qword ptr [rbp+38h],rcx

它这里直接是str实例的MethodTable指针的指针,也即rcx。它这指向指针的指针是哪里来的呢?

二:全局字符串map表
实际上,在托管Main入口之前,

System.Private.CoreLib.dll里面的函数
System.AppContext.setup。对这些字符串进行了实例化。填充MethodTable。同时它拥有一个全局字符串表:m_pGlobalStringLiteralMap

STRINGREF *StringLiteralMap::GetStringLiteral(EEStringData *pStringData, BOOL bAddIfNotFound, BOOL bIsCollectible, void** ppPinnedString)
{
 StringLiteralEntryHolder pEntry(SystemDomain::GetGlobalStringLiteralMap()->GetStringLiteral(pStringData, dwHash, bAddIfNotFound, preferFrozenObjectHeap));
}

GetStringLiteral调用如下

StringLiteralEntry *GlobalStringLiteralMap::GetStringLiteral(EEStringData *pStringData, DWORD dwHash, BOOL bAddIfNotFound, BOOL bPreferFrozenObjectHeap)
{
   pEntry = AddStringLiteral(pStringData, bPreferFrozenObjectHeap);
}

AddStringLiteral如下

StringLiteralEntry *GlobalStringLiteralMap::AddStringLiteral(EEStringData *pStringData, bool preferFrozenObjHeap)
{
   STRINGREF strObj = AllocateStringObject(pStringData, preferFrozenObjHeap, &isFrozen);
}

他这个里面进行了AllocateStringObject,也就是分配字符串和填充MethodTable。那么字符串实际上就是在这里被实例化的。

三:如何赋值给JIT
字符串在托管Main之前被填充了,JIT操作IL的时候需要这个填充的数据。它是怎么获取的呢?

InfoAccessType iat =
        info.compCompHnd->constructStringLiteral(tree->AsStrCon()->gtScpHnd, tree->AsStrCon()->gtSconCPX, &pValue);

以上这段代码 info.compCompHnd里面附带的信息足够填充JIT操作IL进行汇编编译的时候给前面值给它赋值上。

四:变体全局变量内存模型
给上面的示例代码变下,假如说变成如下:

public static readonly List<int> list = new List<int>() { 1, 2, 3, 4 };
static void Main(string[] args)
{
}

那么这个list的内存模型应该如何追踪呢?实际上,Main里面没有任何汇编代码,所以无法查看蛛丝马迹。所以这里可以把list实例在Main里面加一行一代码,让其汇编显示,就可以跟踪了。如下:

public static readonly List<int> list = new List<int>() { 1, 2, 3, 4 };
static void Main(string[] args)
{
   list.Add(5);
}

看下它的汇编

00007FFDF4D606B0 55                   push        rbp  
00007FFDF4D606B1 57                   push        rdi  
00007FFDF4D606B2 56                   push        rsi  
00007FFDF4D606B3 48 83 EC 30          sub         rsp,30h  
00007FFDF4D606B7 48 8B EC             mov         rbp,rsp  
00007FFDF4D606BA 33 C0                xor         eax,eax  
00007FFDF4D606BC 48 89 45 28          mov         qword ptr [rbp+28h],rax  
00007FFDF4D606C0 48 89 4D 50          mov         qword ptr [rbp+50h],rcx  
00007FFDF4D606C4 83 3D DD C8 0B 00 00 cmp         dword ptr [7FFDF4E1CFA8h],0  
00007FFDF4D606CB 74 05                je          Test_.Program.Main(System.String[])+022h (07FFDF4D606D2h)  
00007FFDF4D606CD E8 3E 73 C3 5F       call        00007FFE54997A10  
00007FFDF4D606D2 90                   nop  
00007FFDF4D606D3 48 B9 00 D1 E1 F4 FD 7F 00 00 mov         rcx,7FFDF4E1D100h

最后一行rcx里是静态存放的地址,里面存放着List,int32类型。它的内存如下:

0x00007FFDF4E1D100:  000001cdccc65bc0 0000000000000000 0000000000000000 0000000000000000 000001cdcec01ec0

第五个八字节000001cdcec01ec0如下:

0x000001CDCEC01EC0:  000001cdd140a598 0000000000000000 0000000000000000

第一个八字节000001cdd140a598如下:

0x000001CDD140A598:  00007ffdf4e53df0 000001cdd140a5f8 0000000500000005

它就是list的对象地址,第一个八字节00007ffdf4e53df0是list的MethodTable,第二个八字节000001cdd140a5f8是Int32的MethodTable,第三个八字节0000000500000005第一个00000005代表例子里面的list.add(5)添加的那个5,第二个00000005代表的是list里面总共有5个元素。

继续看第二个八字节00007ffdf4e53df0

0x000001CDD140A5F8  00007ffdf4d4bf38 0000000000000008 0000000200000001 0000000400000003 0000000000000005

可以看到list里的总共分配了8个int32长度的位置,后面跟着{1, 2, 3, 4,5}。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值