知识大全 一步步教你读懂NET中IL
Posted 指令
篇首语:多诈的人藐视学问,愚鲁的人羨慕学问,聪明的人运用学问。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 一步步教你读懂NET中IL相关的知识,希望对你有一定的参考价值。
一步步教你读懂NET中IL 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
接触NET也有 年左右的时间了 NET的内部实现对我产生了很大的吸引力 个人觉得 能对这些底部的实现进行了解和熟练的话 对以后自己写代码是有很大帮助的 好了 废话不多说 请看下边 NET CLR 和 Java VM 都是堆叠式虚拟机器(Stack Based VM) 也就是说 它们的指令集(Instruction Set)都是采用堆叠运算的方式 执行时的资料都是先放在堆叠中 再进行运算 JavaVM 有约 个指令(Instruction) 每个指令都是 byte 的 opcode(操作码) 后面接不等数目的参数 NET CLR 有超过 个指令 但是有些指令使用相同的 opcode 所以 opcode 的数目比指令数略少 特别注意 NET 的 opcode 长度并不固定 大部分的 opcode 长度是 byte 少部分是 byte
下面是一个简单的 C# 原始码
复制代码 代码如下: using System; public class Test public static void Main(String[] args) int i= ; int j= ; int k= ; int answer = i+j+k; Console WriteLine("i+j+k="+answer);将 此原始码编译之后 可以得到一个 EXE的程序 我们可以通过 ILDASM EXE(图 ) 来反编译 EXE 以观察IL 我将 Main() 的 IL 反编译条列如下 这里共有十八道IL 指令 有的指令(例如 ldstr 与 box)后面需要接参数 有的指令(例如 ldc i 与与add)后面不需要接参数 图 ldc i stloc ldc i stloc ldc i stloc ldloc ldloc add ldloc add stloc ldstr "i+j+k=" ldloc box [mscorlib]System Int call string [mscorlib]System String::Concat(object object) call void [mscorlib]System Console::WriteLine(string) ret
此程式执行时 关键的记忆体有三种 分别是 Managed Heap 这是动态配置(Dynamic Allocation)的记忆体 由 Garbage Collector(GC)在执行时自动管理 整个Process 共用一个 Managed Heap
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 都是使用 … 编号的方式来识别区别变数
Evaluation Stack 这是由 NET CLR 在执行时自动管理的记忆体 每个 Thread 都有自己专属的 Evaluation Stack 前面所谓的堆叠式虚拟机器 指的就是这个堆叠
后面有一连串的示意图 用来解说在执行时此三种记忆体的变化 首先 在进入 Main() 之后 尚未执行任何指令之前 记忆体的状况如图 所示
图
接着要执行第一道指令 ldc i 此指令的意思是 在 Evaluation Stack 置入一个 byte 的常数 其值为 执行完此道指令之后 记忆体的变化如图 所示
ldc i 表示加载一个值为 到堆栈中 该条指令的语法结构是 ldc typevalue ldc指令加载一个指定类型的常量到stack ldc i number ldc指令更加有效 它传输一个整型值 以及 到 之间的整数给计算堆栈
图
接着要执行第二道指令 stloc 此指令的意思是 从 Evaluation Stack 取出一个值 放到第 号变数(V )中 这里的第 号变数其实就是原始码中的i 执行完此道指令之后 记忆体的变化如图 所示
图
后面的第三道指令和第五道指令雷同于第一道指令 且第四道指令和第六道指令雷同于第二道指令 为了节省篇幅 我不在此一一赘述 提醒大家第 号变数(V )其实就是原始码中的 j 且第 号变数(V )其实就是源码中的 k 图 ~ 分别是执行完第三~六道指令之后 记忆体的变化图
图
图 图
图
接着要执行第七道指令 ldloc 以及第八道指令 ldloc 分别将 V (也就是 i)和 V (也就是 j)的值放到 Evaluation Stack 这是相加前的准备动作 图 与图 分别是执行完第七 第八道指令之后 记忆体的变化图
图
图
接着要执行第九道指令 add 此指令的意思是 从 Evaluation Stack 取出两个值(也就是 i 和 j) 相加之后将结果放回 Evaluation Stack 中 执行完此道指令之后 记忆体的变化如图 所示
图
接着要执行第十道指令 ldloc 此指令的意思是 分别将 V (也就是 k)的值放到 Evaluation Stack 这是相加前的准备动作 执行完此道指令之后 记忆体的变化如图 所示
图
接着要执行第十一道指令 add 从 Evaluation Stack 取出两个值 相加之后将结果放回 Evaluation Stack 中 此为 i+j+k 的值 执行完此道指令之后 记忆体的变化如图 所示
图
接着要执行第十二道指令 stloc 从 Evaluation Stack 取出一个值 放到第 号变数(V )中 这里的第 号变数其实就是原始码中的 answer 执行完此道指令之后 记忆体的变化如图 所示
图
接着要执行第十三道指令 ldstr "i+j+k=" 此指令的意思是 将 "i+j+k=" 的 Reference 放进 Evaluation Stack 执行完此道指令之后 记忆体的变化如图 所示
图
接着要执行第十四道指令 ldloc 将 V 的值放进 Evaluation Stack 执行完此道指令之后 记忆体的变化如图 所示
图
接着要执行第十五道指令 box [mscorlib]System Int 从此处可以看出 int到string实际是进行了装箱操作的 所以会有性能损失 可以在以后的编码中减少装箱操作来提高性能 此指令的意思是 从 Evaluation Stack 中取出一个值 将此 Value Type 包装(box)成为 Reference Type 执行完此道指令之后 记忆体的变化如图 所示
图
接着要执行第十六道指令 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(呼叫虚拟) 执行完此道指令之后 记忆体的变化如图 所示
图
请注意 此时 Managed Heap 中的 Int ( ) 以及 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 执行完此道指令之后 记忆体的变化如图 所示
图
接着要执行第十八道指令 ret 此指令的意思是 结束此次呼叫(也就是 Main 的呼叫) 此时会检查 Evaluation Stack 内剩下的资料 由于 Main() 宣告不需要传出值(void) 所以 Evaluation Stack 内必须是空的 本范例符合这样的情况 所以此时可以顺利结束此次呼叫 而 Main 的呼叫一结束 程式也随之结束 执行完此道指令之后(且在程式结束前) 记忆体的变化如图 所示
图
cha138/Article/program/net/201311/14024相关参考