详解.NET IL代码

转载:https://blog.csdn.net/CNHK1225/article/details/53569075

 

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

1

2

3

4

5

6

7

8

9

10

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 反编译条列如下,这里共有十八道IL 指令,有的指令(例如 ldstr 与 box)后面需要接参数,有的指令(例如 ldc.i4.1与与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

  此程式执行时,关键的记忆体有三种,分別是:

  1、Managed Heap:这是动态配置(Dynamic Allocation)的记忆体,由 Garbage Collector(GC)在执行时自动管理,整个Process 共用一个 Managed Heap。

  2、Call Stack:这是由 .NET CLR 在执行时自动管理的记忆体,每个 Thread 都有自己专属的 Call Stack。每调用一次 method,就会使得Call Stack 上多了一个 Record Frame;调用完毕之后,此 Record Frame 会被丢弃。一般來说,Record Frame 內记录着 method 参数(Parameter)、返回位址(Return Address)、以及区域变数(Local Variable)。Java VM 和 .NET CLR 都是使用 0, 1, 2… 编号的方式來识別区别变数。

  3、Evaluation Stack:这是由 .NET CLR 在执行时自动管理的记忆体,每个 Thread 都有自己专属的 Evaluation Stack。前面所谓的堆叠式虚拟机器,指的就是这个堆叠。

  后面有一连串的示意图,用來解说在执行时此三种记忆体的变化。首先,在进入 Main() 之后,尚未执行任何指令之前,记忆体的状況如图1 所示:

图1

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

  ldc.i4.1:表示加载一个值为1到堆栈中,该条指令的语法结构是:

  ldc.type value:ldc指令加载一个指定类型(type)值为value的到stack.

  ldc.i4.number:ldc指令更加有效.它传输一个整型值-1以及0到8之间的整数给计算堆栈(其中i4是int32,i8是int64)

图2;

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

图3

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

图4

图5

 图6

 图7

  接着要执行第七道指令 ldloc.0 以及第八道指令 ldloc.1:分別将 V0(也就是 i)和 V1(也就是 j)的值放到 Evaluation Stack,这是相加前的准备动作。图8与图9 分別是执行完第七、第八道指令之后,记忆体的变化图:

图8

 图9

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

 图10

  接着要执行第十道指令 ldloc.2。此指令的意思是:分別将 V2(也就是 k)的值放到 Evaluation Stack,这是相加前的准备动作。执行完此道指令之后,记忆体的变化如图11 所示:

 图11

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

 图12

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

 图13

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

 图14

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

 图15

  接着要执行第十五道指令 box [mscorlib]System.Int32,从此处可以看出:值类型与字符串进行+操作的时候,由于调用的是内部的String.Concat方法,而这个方法是传递(object,object)这2个参数,所以会进行装箱操作(box),因此会有性能上的损失,可以在以后的代码编写中避免不必要的装箱操作。此指令的意思是:从 Evaluation Stack 中取出一个值,将此 Value Type 包裝(box)成为 Reference Type。执行完此道指令之后,记忆体的变化如图16 所示:

 图16

  接着要执行第十六道指令 call string [mscorlib] System.String::Concat(object, object)。此指令的意思是:从 Evaluation Stack 中取出兩个值,此二值皆为 Reference Type,下面的值当作第一个参数,上面的值当作第二个参数,调用 mscorlib.dll 所提供的 System.String.Concat() method 來将此二参数进行字串接合(String Concatenation),将接合出來的新字串放在 Managed Heap,将其 Reference 放进 Evaluation Stack。值得注意的是:由于 System.String.Concat() 是 static method,所以此处使用的指令是 call,而非 callvirt(调用虚拟)。执行完此道指令之后,记忆体的变化如图17 所示:

 图17

  请注意:此时 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

  接着要执行第十八道指令 ret。此指令的意思是:结束此次调用(也就是 Main 的调用)。此时会檢查 Evaluation Stack 內剩下的資料,由于 Main() 宣告不需要传出值(void),所以 Evaluation Stack 內必須是空的,本范例符合这样的情況,所以此时可以顺利结束此次调用。而 Main 的调用一结束,程式也随之结束。执行完此道指令之后(且在程式结束前),记忆体的变化如图19 所示:

 图19

  通过此范例,读者应该可以对于 IL 有最基本的认识。对 IL 感兴趣的读者应该自行阅读 Serge Lidin 所著的《Inside Microsoft .NET IL Assembler》(Microsoft Press 出版)。我认为:熟知 IL 每道指令的作用,是 .NET 程式员必备的知识。.

你可能感兴趣的:(C#,IL,net)