读懂IL代码(二)

上一篇提到了最基本的IL代码,应该是比较通俗易懂的,所以有了上一篇的基础之后,这篇便要深入一点点的来讲述了。

首先我必须再来说一些重要的概念:

Evaluation Stack(评估栈):这是由.NET CLR在执行时候自动管理的记忆体,每一个线程都有自己的评估栈,也就是说,它是用来存储临时变量的线程栈(应该可以这么理解)。值类型存储数据,引用类型存储地址。

Call Stack(调用栈):这也是由.NET CLR在执行时候自动管理的记忆体,每一个线程都有自己的调用栈,每一次调用method,就会产生一个栈帧(stack frame),当方法结束的时候,此栈帧就会被丢弃。

大家一定疑问怎么都是栈呀,堆到哪里去了嘞,大家别急,下面说:

Managed Heap:这是动态配置的记忆体,由Garbage Collector(GC)执行时自动管理,整个程序共用一个。我把这个理解为托管堆,用来存储引用类型的值。

这一篇主要是以引用类型为主来讲述IL代码。

大家都知道把值类型变成引用类型是采用装箱的操作,所以这里也先说一下装箱的过程:

(1)内存分配,在Managed Heap(托管堆)中分配一个内存空间;

(2)将值类型的字段拷贝到分配的内存中;

(3)将托管堆中的对象地址返回给新的对象,Over。

以上是一些基本知识点,现在主要来讲解IL代码:

正规代码如下:

我用Reflector进行查看IL代码:

上图就是IL代码了,经过上一篇的介绍,我觉得这里大家应该也看得懂吧,下面我就来解释这段IL代码吧。

.method private hidebysig static void Main(string[] args)cilmanaged
{
    .entrypoint //入口
    .maxstack 2 //评估栈可容纳数据项的最大个数。
    .locals init (
        [0] string str,
        [1] int32 num) //定义并初始化参数,并存入局部变量表(Call Stack)中。
    L_0000: nop //No Operation
    L_0001: ldstr "Helius" //将字符串“Helius”压入评估栈中。
    L_0006: stloc.0  //将字符串从评估栈中弹出,赋值给局部变量表的第0个变量。
    L_0007: ldc.i4.s 0x1b //解析:int类型的数值大小不一样,IL代码也是不一样的。比如int i=-1;IL代码为ldc.i4.M1;当i的值大于等于9的时候,IL代码就为ldc.i4.s(i的十六进制表示形式),当i的值小于等于-2时,IL代码表示为ldc.i4.s(i)。
    L_0009: stloc.1 //将值从评估栈中弹出,赋值给局部变量表的第1个变量。
    L_000a: ldloca.s num //取出局部变量表中num的值“27”并压入评估栈中。
    L_000c: call instance string [mscorlib]System.Int32::ToString() //从评估栈中取出数值“27”,调用ToString方法转成string类型并将引用存入评估栈中。
    L_0011: ldloc.0 //取出局部变量表中的第0个位置元素值“Helius”,并压入评估栈,(此时评估栈中有两个值,字符串“Helius”和“27”的引用地址)。
    L_0012: call string [mscorlib]System.String::Concat(string, string) //调用String类的Concat方法把字符串拼接并存入托管堆中,把引用地址返回给评估栈。
    L_0017: call void [mscorlib]System.Console::WriteLine(string) //调用输出方法,调用输出方法后评估栈中的值(指向托管堆中字符的地址)会被回收。
    L_001c: nop  ------------------------------------------分割线---------------------------------------------------------------------------------------------------
    L_001d: ldloc.1 //取局部变量表中的第1个位置参数num值,存入评估栈中。
    L_001e: box int32 //把num值27装箱,并返回托管堆中的地址存入评估栈中。
    L_0023: ldloc.0 //去局部变量表中的第0个参数,并压入评估栈中。
    L_0024: call string [mscorlib]System.String::Concat(object, object)//弹出评估栈中两个值,并调用String的Concat方法把字符拼接,存入托管堆中,并返回引用地址到评估栈中。
    L_0029: call void [mscorlib]System.Console::WriteLine(string) //调用输出方法。
    L_002e: nop
    L_002f: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()//调用ReadKey方法
    L_0034: pop //把评估栈的内容清空
    L_0035: ret //return 标记返回
}

以图来解释上面的例子可能更明白一点(程序员学会画图很重要,我记得去面试的时候,面试官还叫我画项目的架构图):图1

图2

图3

从以上的图中可以明显看出调用方法与装箱的流程。引用类型都是存放在托管堆里面的,而栈只存放引用类型的地址,这点是要特别注意的。

下一篇我会写方法,委托和类的IL代码解析。

时间: 2024-10-13 00:20:20

读懂IL代码(二)的相关文章

读懂IL代码就这么简单(二)

一 前言 IL系列 第一篇写完后 得到高人指点,及时更正了文章中的错误,也使得我写这篇文章时更加谨慎,自己在了解相关知识点时,也更为细致.个人觉得既然做为文章写出来,就一定要保证比较高的质量,和正确率 .感谢 @冰麟轻武 的指点 你没有看第一篇?  点这里看第一篇 读懂IL代码就这么简单(一) IL指令大全 :IL指令详解 IL反编译工具: ILDasm 知识点回顾: Managed Heap(托管堆):用于存放引用类型的值 Evaluation Statck(计算栈):临时存放值类型数据,引用

读懂IL代码就这么简单(一)

一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉厉. 然后开始接触IL,了解了一段时后才发现原来读懂IL代码并不难.进入正题   1.1  什么是IL IL是.NET框架中中间语言(Intermediate Language)的缩写.使用.NET框架提供的编译器可以直接将源程序编译为.exe或.dll文件,但此时编译出来的程序代码并不是CPU能直

读懂IL代码就这么简单(三)完结篇

原文:读懂IL代码就这么简单(三)完结篇 一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍 这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在的层次,能理解到的都写完了,而且个人认为,重要的地方都差不多 写到了, 最后一篇决定把之前的内容全部整合起做一个综合的例子,然后简单的解释下IL指令的含义,及在内存中的变化 如果你没有看前两篇请狂点这里 读懂IL代码就这么简单 (一) 读懂IL代码就这么简单(二) IL指令大全 :IL指令详解

写让别人能读懂的代码

随着软件行业的不断发展,历史遗留的程序越来越多,代码的维护成本越来越大,甚至大于开发成本.而新功能的开发又常常依赖于旧代码,阅读旧代码所花费的时间几乎要大于写新功能的代码. 我前几天看了一本书,书中有这么一句话: “复杂的代码往往都是新手所写,只有经验老道的高手才能写出简单,富有表现力的代码” 此话虽然说的有点夸张,可是也说明了经验的重要性. 我们所写的代码除了让机器执行外,还需要别人来阅读.所以我们要: 写让别人能读懂的代码 写可扩展的代码 写可测试的代码(代码应该具备可测试性,对没有可测试性

(转)写让别人能读懂的代码

随着软件行业的不断发展,历史遗留的程序越来越多,代码的维护成本越来越大,甚至大于开发成本.而新功能的开发又常常依赖于旧代码,阅读旧代码所花费的时间几乎要大于写新功能的代码. 我前几天看了一本书,书中有这么一句话: “复杂的代码往往都是新手所写,只有经验老道的高手才能写出简单,富有表现力的代码” 此话虽然说的有点夸张,可是也说明了经验的重要性. 我们所写的代码除了让机器执行外,还需要别人来阅读.所以我们要写: 让别人能读懂的代码 可扩展的代码 可测试的代码(代码应该具备可测试性,对没有可测试性的代

夯实Java基础系列7:一文读懂Java 代码块和执行顺序

目录 Java中的构造方法 构造方法简介 构造方法实例 例 1 例 2 Java中的几种构造方法详解 普通构造方法 默认构造方法 重载构造方法 java子类构造方法调用父类构造方法 Java中的代码块简介 Java代码块使用 局部代码块 构造代码块 静态代码块 Java代码块.构造方法(包含继承关系)的执行顺序 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github

(转) 读懂IL

引言 转自园子里的一片关于IL的好文,分享的同时,方便自己今后查阅. 原文链接:http://www.cnblogs.com/brookshi/p/5225801.html ------ 略过作者调侃内容,直接进入干活部分! 将IL用法分为三类,如下. 第一类 :直观型 这一类的特点是一看名字就知道是干嘛的,不需要多讲,如下: 名称 说明 Add 将两个值相加并将结果推送到计算堆栈上. Sub 从其他值中减去一个值并将结果推送到计算堆栈上. Div 将两个值相除并将结果作为浮点(F 类型)或商(

如何看懂IL代码--转载

1//示例一:输出整数的立方值.  2private void PrintCube( int i )  3{  4    int cube = i * i * i;  5    Console.WriteLine( cube );  6}  7//方法签名.  8/// hidebysig:MethodAttributes 枚举值之一,指示此方法按名称和签名隐藏,否则只  9/// 按名称隐藏. 10/// cil managed:未查到具体资料,应是“受中间语言管理”之意. 11 12.met

浅析.NET IL代码

一.前言 IL是什么? Intermediate Language (IL)微软中间语言 C#代码编译过程? C#源代码通过LC转为IL代码,IL主要包含一些元数据和中间语言指令: JIT编译器把IL代码转为机器识别的机器代码.如下图 语言编译器:无论是VB code还是C# code都会被Language Compiler转换为MSIL MSIL的作用:MSIL包含一些元数据和中间语言指令 JIT编译器的作用:根据系统环境将MSIL中间语言指令转换为机器码 为什么ASP.NET网站第一次运行时