Mih*_*ert 7 c# stack cil heap-memory .net-assembly
让我们考虑一些非常简单的C#代码:
static void Main(string[] args)
{
int i = 5;
string s = "ABC";
bool b = false;
}
Run Code Online (Sandbox Code Playgroud)
Jeffrey Richter的“ 通过C#进行CLR ”(第14章)指出,“ String类型是直接从Object派生的,使其成为引用类型,因此,String对象(其字符数组)始终存在于堆中,而不是在线程的堆叠 ”。
在书中的一个示例上也与上面的示例非常相似,它也涉及字符串:“ newobj IL指令构造了一个对象的新实例。但是,IL代码示例中没有newobj指令出现。相反,您看到了特殊的ldstr (负载字符串)IL指令,其通过使用从元数据得到的文字串构成一个字符串对象。这显示了公共语言运行库(CLR)不,事实上,具有构造文字串对象的一种特殊的方式。 “
查看IL代码,显然是这种情况(仅显示了相关部分):
[...]
.locals init (
[0] int32,
[1] string,
[2] bool
)
// (no C# code)
IL_0000: nop
// int num = 5;
IL_0001: ldc.i4.5
IL_0002: stloc.0
// string text = "ABC";
IL_0003: ldstr "ABC"
IL_0008: stloc.1
// bool flag = false;
[...]
Run Code Online (Sandbox Code Playgroud)
所述ldstrIL指令确保了 “ 的对象引用的字符串被压入堆栈 ”。这很有意义-字符串的实例保留在堆上,并且对该对象(其地址)的引用由变量存储在堆栈上。
现在,让我们在text要声明的变量之后的行上设置一个断点,在Visual Studio中开始调试,然后切换到“反汇编”视图。相关代码如下(完整的反汇编代码在此处):
017B0483 nop
int i = 5;
017B0484 mov dword ptr [ebp-40h],5
string s = "ABC";
017B048B mov eax,dword ptr ds:[429231Ch]
017B0491 mov dword ptr [ebp-44h],eax
bool b = false;
017B0494 xor edx,edx
017B0496 mov dword ptr [ebp-48h],edx
}
Run Code Online (Sandbox Code Playgroud)
在2个汇编指令处理C#寻找具体string行,第一个移动的虚拟内存在内容429231C的eax寄存器,和所述第二存储堆叠,所述其中在相应的内容s可变生命。
让我们使用WinDbg(x86,因为C#代码使用VS的默认32位目标平台)通过以非侵入方式附加到VS调试的进程来查看该特定地址。429231C上面的内容应该是对字符串实际所在的存储空间的引用。让我们检查:
第二个命令的确会产生一个41,42并43以十六进制表示A,B并C以ASCII 表示;但是顺序并不正确,可能只是巧合。(1)看起来好像字符串行的汇编代码没有做对。
原始地址429231C看起来在托管堆中。但是,然后(2)为什么将堆中地址的内容作为包含在堆栈变量中的引用引入,如先前的汇编代码所示?
在2问题小号我问是(1)和(2)。尽管事实上在分析IL代码之前一切对我来说都是有意义的,但是一旦我查看了该IL的反汇编代码,事情就会很快走下坡路。我倾向于以为我在逻辑上搞砸了(很可能),或者在VS调试器中碰到了某种错误(不太可能)。
以后的更新:正如@madreflection和@Jester所指出的那样,字节序使我绊倒了。十六进制表示可以检出所有内容。现在仅保留问题(2)。
以后的更新2:评论非常有见地,我认为@madreflection最好说-有一个间接的附加级别-这样做的原因(评论中指出)现在对我来说很有意义。下面是一个快速图表。我还检查了两个地址是否确实属于VMMap的托管堆。
以后的更新3:更正了之前的图。
| 归档时间: |
|
| 查看次数: |
164 次 |
| 最近记录: |