手机版

教你一步一步读. NET中的IL

时间:2021-11-18 来源:互联网 编辑:宝哥软件园 浏览:

我一直在联系。NET运行了大约一年,并在内部实现了。NET极大地吸引了我。个人认为,如果你能理解并精通这些底层实现,对你以后自己编写代码会有很大的帮助。好了,我们不多说了。请参见以下内容:NET CLR和Java VM都是基于栈的VM,也就是说,它们的指令集都采用栈操作的方式:执行时的数据在操作前放在栈上。JavaVM大约有200个指令,每个指令都是一个1字节的操作码,后跟不等数量的参数。那个。NET CLR有220多条指令,但有些指令使用相同的操作码,因此操作码的数量略少于指令的数量。尤其是的操作码长度。NET不是固定的,大多数操作码都是1字节长,少数是2字节长。下面是一个简单的C#源代码:复制代码如下:使用System公共类Test { public static void Main(String[]args){ int I=1;int j=2;int k=3;int答案=I j k;控制台。WriteLine('i j k='答案);}}编译完这个源代码,就可以得到一个EXE程序。我们可以通过ILDASM.EXE反编译EXE(图-0)来观察IL。我将Main()的IL反编译列出如下。这里有十八个il指令,有些指令(比如ldstr和box)需要跟在参数后面,有些指令(比如ldc.i4.1和add)不需要跟在参数后面。

图-0最不发达国家。i4.1 STLOC.0 LDC。i4.2 STLOC.1 LDC。I 4.3 STLOC . 2 LDLOC . 0 LDLOC . 1 ADDROCL . 2 ADDROCL . 3 LDSTR ' I J K=' LDLOC . 3 Box[mscorlib]。系统。int32callstring [mscorlib]系统。string:3360 concat (object,object)调用void [mscorlib]系统。console : writeline(string)ret当此程序运行时,内存有三种关键类型,分别是:1。托管堆:这是动态分配的内存,在执行过程中由垃圾收集器(GC)自动管理,整个进程共享一个托管堆。2.调用堆栈:这是由自动管理的内存。每个线程都有自己的调用栈。每次调用该方法时,都会向调用堆栈中添加一个记录帧。呼叫完成后,该记录帧将被丢弃。一般来说,一个方法参数、一个返回地址和一个局部变量被记录在一个记录帧中。Java虚拟机和。NET CLR使用0,1,2…数字来标识可分辨变量。3.评估堆栈:这是由自动管理的内存。NET CLR,每个线程都有自己的评估堆栈。所谓的堆栈虚拟机就是指这个堆栈。有一系列的图表来解释这三种记忆在执行过程中的变化。首先,进入Main()后,执行任何指令前,内存状态如图1所示:對 1

图1接下来,将执行第一指令ldc.i4.1。该指令的含义是在评估堆栈中放入一个值为1的4字节常量。执行此指令后,内存发生变化,如图2所示:ldc.i4.1:意味着将值1加载到堆栈中。此指令的语法结构为:ldc.typevalue:ldc指令将指定类型的常量加载到stack.ldc.i4.number:ldc指令更有效。它向计算堆栈對 2传输-1的整数值和0到8之间的整数。

图2接下来,将执行第二指令stloc.0。这个命令的意思是:从评估堆栈中获取一个值,并将其放入0号变量(V0)。这里的0号变量实际上是源代码中的I。执行此命令后,内存发生变化,如图3所示:對 3

图3后面的第三和第五条指令类似于第一条指令,第四和第六条指令类似于第二条指令。为了节省篇幅,我在这里不再重复。提醒大家一号变量(V1)其实是源代码中的J,二号变量(V2)其实是源代码中的K。图4-7是执行第三至第六指令后的存储器变化:對 4

图4對 5

图5對 6

图6對 7

图7接下来,将执行第七条指令ldloc.0和第八条指令ldloc.1:将V0(即I)和V1(即j)的值分别放在评估堆栈上,这是添加前的准备动作。图8和图9分别是执行第七和第八条指令后的内存变化:對 8

图8對 9

图9接下来,执行第九条指令add。这条指令的意思是:从评估堆栈中取出两个值(即I和j),将它们相加,并将结果放回评估堆栈。执行该命令后,内存发生变化,如图10所示:對 10

图10接下来,将执行第十指令ldloc.2。该指令的含义是将V2(即k)的值分别放在评估堆栈上,这是添加前的准备动作。执行该命令后,内存发生变化,如图11所示:對 11

图11是执行第十一指令add。从Evaluation Stack中取两个值,相加后将结果放回Evaluation Stack,就是i j k的值,执行这个命令后,内存发生变化,如图12所示:對 12

图12接下来,将执行第十二指令stloc.3。从评估堆栈中获取一个值,并将其放入第三个变量(V3)中。这里的第三个变量实际上是源代码中的答案。执行该命令后,内存发生变化,如图13所示:對 13

图13接下来,将执行第十三指令ldstr 'i j k='。该命令的含义是将“i j k=”的引用放入评估堆栈。执行该指令后,内存发生变化,如图14所示:對 14

图14接下来,将执行第14条指令ldloc.3。将V3的值放入评估堆栈。执行该命令后,内存发生变化,如图15所示:對 15

在图15中,第15个指令盒[mscorlib]系统。Int32将被执行。由此可以看出,int to string实际上是装箱的,所以会有性能损失,在以后的编码中可以减少装箱操作来提高性能。此指令的含义是从评估堆栈中获取一个值,并将此值类型框入引用类型。执行该指令后,内存发生变化,如图16所示:對 16

图16是执行第16条指令call string[mscorlib]system . string :3360 concat(object,object)。该指令的含义是从评估堆栈中取出两个值,这两个值都是引用类型,下面的值作为第一个参数,上面的值作为第二个参数。给系统打电话。方法对这两个参数执行字符串连接,将连接的新字符串放入托管堆,并将其引用放入评估堆栈。值得注意的是,自系统。Concat()是一个静态方法,这里使用的指令是call而不是callvirt。执行该命令后,内存发生变化,如图17所示:對 17

图17请注意:此时托管堆中的Int32(6)和String('i j k=')不再被引用,所以变成了垃圾,等待GC回收。然后执行第17条指令调用void[mscorlib]system . console :3360 write line(字符串)。这个命令的意思是:从求值栈中取一个值,就是引用类型,把这个值作为参数,调用系统。方法在控制台窗口上显示该字符串。系统。Console.WriteLine()也是一个静态方法。执行该指令后,内存发生变化,如图18所示:對 18

图18是执行第十八指令ret。这个命令的意思是:结束这个调用(即Main的调用)。此时,将检查评估堆栈中的剩余数据。由于main()声明不需要void,因此评估堆栈必须为空。这个例子符合这种情况,所以此时可以成功结束呼叫。对Main的调用一结束,程序就结束了。执行该命令后(程序结束前),内存变化如图19所示:對 19

图19通过这个例子,读者应该对IL有了最基本的了解。对IL感兴趣的读者请自行阅读Serge Lidin的《Inside Microsoft .NET IL Assembler》(微软出版社出版)。在我看来,这是必要的。NET程序员来了解IL的每个指令的功能。那个。NET程序员可能不会用IL Assembly写,但至少他必须了解ILDASM反编译的IL汇编代码。

版权声明:教你一步一步读. NET中的IL是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。