欢迎来到代码驿站!

.NET代码

当前位置:首页 > 软件编程 > .NET代码

一步步教你读懂NET中IL(图文详解)

时间:2021-01-01 15:08:26|栏目:.NET代码|点击:

接触NET也有1年左右的时间了,NET的内部实现对我产生了很大的吸引力。个人觉得:能对这些底部的实现进行了解和熟练的话,对以后自己写代码是有很大帮助的,好了,废话不多说,请看下边:

.NET CLR 和 Java VM 都是堆叠式虚拟机器(Stack-Based VM),也就是?f,它??的指令集(Instruction Set)都是?裼枚训?运算的方式:执行时的资料都是先放在堆叠中,再进行运算。JavaVM 有?s 200 ??指令(Instruction),每??指令都是 1 byte 的 opcode(操作码),后面接不等数目的参数;.NET CLR 有超?^ 220??指令,但是有些指令使用相同的 opcode,所以 opcode 的数目比指令数略少。特?e注意,.NET 的 opcode ?L度?K不固定,大部分的 opcode ?L度是 1 byte,少部分是 2 byte。

下面是一??简单的 C# 原始码:                

复制代码 代码如下:

using System;
public class Test {
    public static void Main(String[] args) {
        int i=1;
        int j=2;
        int k=3;
        int answer = i+j+k;
        Console.WriteLine("i+j+k="+answer);
    }
}

?⒋嗽?始码编译之后,可以得到一?? EXE的程序。我??可以通过 ILDASM.EXE(图-0) ?矸幢嘁? EXE 以观察IL。我?? Main() 的 IL 反编译条列如下,?@?e共有十八道IL 指令,有的指令(例如 ldstr 与 box)后面需要接参数,有的指令(例如 ldc.i4.1 ?c与add)后面不需要接参数。


图-0
ldc.i4.1
stloc.0
ldc.i4.2
stloc.1
ldc.i4.3
stloc.2
ldloc.0
ldloc.1
add
ldloc.2
add
stloc.3
ldstr      "i+j+k="
ldloc.3
box        [mscorlib]System.Int32
call       string [mscorlib]System.String::Concat(object, object)
call       void [mscorlib]System.Console::WriteLine(string)
ret

此程式执行?r,关键的记忆体有三种,分?e是:

1、Managed Heap:?@是动态配置(Dynamic Allocation)的记忆体,由 Garbage Collector(GC)在执行?r自?庸芾恚?整??Process 共用一?? Managed Heap。

2、Call Stack:?@是由 .NET CLR 在执行?r自?庸芾淼募且涮澹?每?? Thread 都有自己专属的 Call Stack。每呼叫一次 method,就会使得Call Stack 上多了一?? Record Frame;呼叫完毕之后,此 Record Frame 会被丢弃。一般?碚f,Record Frame ?燃锹甲? method 参数(Parameter)、返回位址(Return Address)、以及区域变数(Local Variable)。Java VM 和 .NET CLR 都是使用 0, 1, 2… 编号的方式?碜R?e区别变数。

3、Evaluation Stack:?@是由 .NET CLR 在执行?r自?庸芾淼募且涮澹?每?? Thread 都有自己专属的 Evaluation Stack。前面所?^的堆叠式虚拟机器,指的就是?@??堆叠。

后面有一?B串的示意图,用?斫庹f在执行?r此三种记忆体的变化。首先,在?M入 Main() 之后,尚未执行任何指令之前,记忆体的??r如图1 所示:

?? 1

图1                

接着要执行第一道指令 ldc.i4.1。此指令的意思是:在 Evaluation Stack 置入一?? 4 byte 的常数,其值?? 1。执行完此道指令之后,记忆体的变化如图2 所示:

ldc.i4.1:表示加载一个值为1到堆栈中,该条指令的语法结构是:
ldc.typevalue:ldc指令加载一个指定类型的常量到stack.
ldc.i4.number:ldc指令更加有效.它传输一个整型值-1以及0到8之间的整数给计算堆栈

?? 2

图2       

接着要执行第二道指令 stloc.0。此指令的意思是:从 Evaluation Stack 取出一??值,放到第 0 号变数(V0)中。?@?e的第 0 号变数其实就是原始码中的i。执行完此道指令之后,记忆体的变化如图3 所示:

?? 3

图3                

后面的第三道指令和第五道指令雷同於第一道指令,且第四道指令和第六道指令雷同於第二道指令。?榱私谑∑?幅,我不在此一一?述。提醒大家第 1 号变数(V1)其实就是原始码中的 j,且第 2 号变数(V2)其实就是源码中的 k。图4~7 分?e是执行完第三~六道指令之后,记忆体的变化图:

?? 4

图4                

?? 5

图5

?? 6
图6

?? 7
图7

接着要执行第七道指令 ldloc.0 以及第八道指令 ldloc.1:分?e?? V0(也就是 i)和 V1(也就是 j)的值放到 Evaluation Stack,?@是相加前的准备?幼鳌M?8 ?c图9 分?e是执行完第七、第八道指令之后,记忆体的变化图:

?? 8

图8

?? 9
图9

接着要执行第九道指令 add。此指令的意思是:从 Evaluation Stack 取出???值(也就是 i 和 j),相加之后?⒔Y果放回 Evaluation Stack 中。执行完此道指令之后,记忆体的变化如图10 所示:

?? 10
图10

接着要执行第十道指令 ldloc.2。此指令的意思是:分?e?? V2(也就是 k)的值放到 Evaluation Stack,?@是相加前的准备?幼鳌V葱型甏说乐噶钪?后,记忆体的变化如图11 所示:

?? 11
图11

接着要执行第十一道指令 add。从 Evaluation Stack 取出???值,相加之后?⒔Y果放回 Evaluation Stack 中,此?? i+j+k 的值。执行完此道指令之后,记忆体的变化如图12 所示:

?? 12
图12

接着要执行第十二道指令 stloc.3。从 Evaluation Stack 取出一??值,放到第 3 号变数(V3)中。?@?e的第3号变数其实就是原始码中的 answer。执行完此道指令之后,记忆体的变化如图13 所示:

?? 13
图13

接着要执行第十三道指令 ldstr "i+j+k="。此指令的意思是:?? "i+j+k=" 的 Reference 放?M Evaluation Stack。执行完此道指令之后,记忆体的变化如图14 所示:

?? 14
图14

接着要执行第十四道指令 ldloc.3。?? V3 的值放?M Evaluation Stack。执行完此道指令之后,记忆体的变化如图15 所示:

?? 15
图15

接着要执行第十五道指令 box [mscorlib]System.Int32,从此处可以看出,int到string实际是进行了装箱操作的,所以会有性能损失,可以在以后的编码中减少装箱操作来提高性能。此指令的意思是:从 Evaluation Stack 中取出一??值,?⒋? Value Type 包?b(box)成?? Reference Type。执行完此道指令之后,记忆体的变化如图16 所示:

?? 16
图16

接着要执行第十六道指令 call string [mscorlib] System.String::Concat(object, object)。此指令的意思是:从 Evaluation Stack 中取出???值,此二值皆?? Reference Type,下面的值当作第一??参数,上面的值当作第二??参数,呼叫 mscorlib.dll 所提供的 System.String.Concat() method ??⒋硕?参数?M行字串接合(String Concatenation),?⒔雍铣?淼男伦执?放在 Managed Heap,?⑵? Reference 放?M Evaluation Stack。值得注意的是:由於 System.String.Concat() 是 static method,所以此?使用的指令是 call,而非 callvirt(呼叫虚拟)。执行完此道指令之后,记忆体的变化如图17 所示:

?? 17
图17

?注意:此?r Managed Heap 中的 Int32(6) 以及 String("i+j+k=") 已?不再被?⒖嫉剑?所以变成垃圾,等待 GC 的回收。

接着要执行第十七道指令 call void [mscorlib] System.Console::WriteLine(string)。此指令的意思是:从 Evaluation Stack 中取出一??值,此值?? Reference Type,?⒋酥档弊鞑问?,呼叫 mscorlib.dll 所提供的 System.Console.WriteLine() method ??⒋俗执?显示在 Console ?窗上。System.Console.WriteLine() 也是 static method。执行完此道指令之后,记忆体的变化如图18 所示:

?? 18 图18

接着要执行第十八道指令 ret。此指令的意思是:?Y束此次呼叫(也就是 Main 的呼叫)。此?r会?z查 Evaluation Stack ?仁O碌馁Y料,由於 Main() 宣告不需要传出值(void),所以 Evaluation Stack ?缺仨?是空的,本范例符合?@?拥那?r,所以此?r可以?利?Y束此次呼叫。而 Main 的呼叫一?Y束,程式也随之?Y束。执行完此道指令之后(且在程式?Y束前),记忆体的变化如图19 所示:

?? 19 图19

通过此范例,读者应该可以对于 IL 有最基本的认识。对 IL 感兴趣的读者应该自行阅读 Serge Lidin 所著的《Inside Microsoft .NET IL Assembler》(Microsoft Press 出版)。我认为:熟知 IL 每道指令的作用,是 .NET 程式?T必备的知识。.NET 程式?T可以不会用 IL Assembly 写程式,但是至少要看得懂 ILDASM 反编译出?淼? IL ?M合码。

上一篇:C#自定义控件实现TextBox禁止粘贴的方法

栏    目:.NET代码

下一篇:C# 委托的三种调用示例(同步调用 异步调用 异步回调)

本文标题:一步步教你读懂NET中IL(图文详解)

本文地址:http://www.codeinn.net/misctech/39254.html

推荐教程

广告投放 | 联系我们 | 版权申明

重要申明:本站所有的文章、图片、评论等,均由网友发表或上传并维护或收集自网络,属个人行为,与本站立场无关。

如果侵犯了您的权利,请与我们联系,我们将在24小时内进行处理、任何非本站因素导致的法律后果,本站均不负任何责任。

联系QQ:914707363 | 邮箱:codeinn#126.com(#换成@)

Copyright © 2020 代码驿站 版权所有