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

摘要:
3.将托管堆中的对象地址返回到新对象的操作过程如下图所示:C#代码1/*2Author:

一 前言

  IL系列 第一篇写完后 得到高人指点,及时更正了文章中的错误,也使得我写这篇文章时更加谨慎,自己在了解相关知识点时,也更为细致。个人觉得既然做为文章写出来,就一定要保证比较高的质量,和正确率 。感谢 @冰麟轻武 的指点

你没有看第一篇?  点这里看第一篇 读懂IL代码就这么简单(一)  

IL指令大全 :IL指令详解

IL反编译工具: ILDasm

知识点回顾:

Managed Heap(托管堆):用于存放引用类型的值

Evaluation Statck(计算栈):临时存放值类型数据,引用类型地址的堆栈(这个是栈,所以遵循栈的操作特点,先进后出)

Call Stack(调用栈):其中的Record Frame 用于存放.locals init(int32 V_0)指令的参数值如:V_0 (Record Frame是一个局部变量表,所以不遵守FILO原则 )

二 指令详解(基本介绍)

2.1 知识点介绍

  在第一篇时,我只详细的写了值类型的IL指令,这一篇会主要以引用类型为主,这一篇会有装箱操作,所以先写一下装箱操作在内存中是如何操作的

装箱操作:1 内存分配,在托管堆中分配内存空间,2 将值类型的字段拷贝到新分配的内存中,3 将托管堆中的对象地址返回给新的对象

操作过程如下图

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

C#代码 

 1         /*
 2          Author:zery-zhang
 3          BlogAddress:http://www.cnblogs.com/zery/
 4          */
 5         static void Main(string[] args)
 6         {
 7             string name = "Zery";
 8             int age = 22;
 9             Console.WriteLine(age.ToString() + name);//已ToString的操作
10             Console.WriteLine(age+name);//未ToString操作
11 
12         }

IL代码 

 1     /*
 2         Author:zery-zhang
 3         BlogAddress:http://www.cnblogs.com/zery/
 4     */
 5     .method private hidebysig static void  Main(string[] args) cil managed
 6 {
 7   .entrypoint
 8   // Code size       48 (0x30)
 9 
10  //以下代码 完成 C#代码中初始化变量的操作
11 
12   //计算栈(Evaluation Stack) 可容纳数据项的最大个数
13   .maxstack  2              
14    //定义并初始化参数 并存入 局部变量表(Record Frame)中
15   .locals init (string V_0,int32 V_1) 
16   IL_0000:  nop
17   //把字符串压入计算栈(Evaluation Stack)中
18   IL_0001:  ldstr      "Zery"   
19   // 从计算栈中弹出("Zery")字符,并赋值给局部变量表中第0个位置的元素V_0
20   IL_0006:  stloc.0       
21   //把整数22压入计算栈中  
22   IL_0007:  ldc.i4.s   22    
23   //把整数22弹出,并赋值给局部变量表中第1个位置的元素V_1
24   IL_0009:  stloc.1             
25 
26   //以下代码完成C#中的输出操作
27 
28   //取出局部变量表中V_1元素的值 "22" (copy)并压入计算栈中
29   IL_000a:  ldloca.s   V_1 
30   //弹出刚刚压入的值("22")调用ToString方法转成string类型并将引用存入计算栈中
31   IL_000c:  call       instance string [mscorlib]System.Int32::ToString() 
32    //取出局部变量表中第0个位置元素(V_0)的值("Zery")压入计算栈中(此时计算栈中有两个值,指向推管堆中"22"的引用地址和字符串"Zery")
33   IL_0011:  ldloc.0            
34    //弹出计算栈中两个值调用String的Concat方法把字个字符拼接存入托管堆中(Managed Heap )并返回地址压入计算栈中
35   IL_0012:  call       string [mscorlib]System.String::Concat(string,string)   
36    //调用输出方法,调用输出方法后计算栈中的值(指向托管堆字符的地址)会被回收 。
37   IL_0017:  call       void [mscorlib]System.Console::WriteLine(string) 
38 
39   //未ToString的操作
40   IL_001c:  nop
41    //取局部变量表中第1个位置的元素V_1的值("22") 压入计算栈中
42   IL_001d:  ldloc.1              
43    //把刚刚压入的整数22 装箱并返回指向托管堆的地址存入计算栈中
44   IL_001e:  box        [mscorlib]System.Int32   
45    //取局部变量表中第0个位置的f元素V_0的值("Zery")并压入计算栈中
46   IL_0023:  ldloc.0              
47    //弹出计算栈中两个值调用String的Concat方法把字个字符拼接存入托管堆中(Managed Heap )并返回地址压入计算栈中
48   IL_0024:  call       string [mscorlib]System.String::Concat(object,object)   
49    //调用输出方法
50   IL_0029:  call       void [mscorlib]System.Console::WriteLine(string)        
51   IL_002e:  nop
52    //标记返回
53   IL_002f:  ret               
54 } // end of method Program::Main

2.2 IL指令详解

  .maxstack:计算栈(Evaluation Stack)可容纳数据项的最大个数

  .locals init (int32 V_0,int32  V_1,int32 V_2):定义变量并存入Call Stack中的Record Frame中

  nop:即No Operation 没有任何操作,我们也不用管它,

  ldstr.:即Load String 把字符串加压入Evaluation Stack中 

  stloc.:把Evaluation Stack中的值弹出赋值到Call Stack中的Record Frame中

  ldloc.:把Call Stack里的Record Frame中指定位置的值取出(copy)存入 Evaluation Stack中   以上两条指令为相互的操作stloc赋值,ldloc取值

  call:  调用指定的方法

  box:执行装箱操作

  ret: 即return  标记返回

三 指令详解(深入介绍)

如果看代码中的注释你还不是很理解,那就看看下面的图解过程吧,如果每一步都画图,那工程太大了,所以我会把简单的的步组合成一张图并做上注释

先画初始化的代码详解图  注:为了减少图片所以栈的弹出与压入操作就省去了,都只画出了结果

IL_0001: ldstr "Zery"
IL_0006: stloc.0
IL_0007: ldc.i4.s 22
IL_0009: stloc.1

因为字符串是引用类型,所以是保存在托管堆中,而栈中只保存对字符引用的地址,可以看到图中的字符串是在托管中的,而计算栈中只保存了引用

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

IL_000a: ldloca.s V_1
IL_000c: call instance string [mscorlib]System.Int32::ToString()
IL_0011: ldloc.0
IL_0012: call string [mscorlib]System.String::Concat(string,string)
IL_0017: call void [mscorlib]System.Console::WriteLine(string)

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

IL_001d: ldloc.1
IL_001e: box [mscorlib]System.Int32
IL_0023: ldloc.0
IL_0024: call string [mscorlib]System.String::Concat(object,
object)
IL_0029: call void [mscorlib]System.Console::WriteLine(string)

装箱的过程图在上面已给出此处只把结果画出

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

四 总结

  1 用两篇把 值类型与引用类型在内存中不同的位置与不同的操作详细的写了,我觉得还是很有必要的,因为所有的数据类型由这两种类型组成,把这两种类型的操作了解了

看其它IL指令就是透过本质看现象了。

     2  关于画图,我觉得画图是程序员必学的知识,牛X的程序员画出来的,系统架构图,系统设计图等都是很有结构很清晰的。我的画图技能还有太多不足,只是画这种简单的图都觉得,无法完美的表达自己头脑所想的那样,  

    

     如果觉得文章与给您带来一点 收获 那就 帮忙点个推荐吧,也让更多的人能关注并了解IL  您的推荐是我源源不断的写作力

     如果您希望您的技术路上能有更多的朋友,那就关注一下吧

    注:本人不才,水平有限,如有不对之处,希望能及时提出,我会马上更正,以免误导他人 谢谢!读懂IL代码就这么简单(二)第5张

   成长在于积累

免责声明:文章转载自《读懂IL代码就这么简单(二)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇wordpress二次开发随笔-2MySQL sql语句获取当前日期|时间|时间戳下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

关于三级联动或者四级联动等的一些思考

原理概述如图 1、当页面初始化时应当初始化的数据源: I级数据源、II级数据源、III级数据源、IV级数据源。。。。。。 所以在初始化I级数据源的时候,需要把II、III、IV级的数据源也初始化了 初始化II级的数据源时,需要把III、IV级的也初始化了,以次类推。。。。。。 最重要的时候在初始化页面时要把四级都初始化了,本人在此出现过错误,记录一下,提...

Redis bigkey分析

一 现象 某个业务最近2个月每月1号凌晨0点都有业务高峰,但是业务所使用的 Redis 服务 cpu 负载100% ,无法对外提供服务进而影响整体业务访问。 二 分析 2.1 问题分析 因为该业务使用的是云Redis ,我们通过监控看 CPU,QPS ,带宽。   出现问题时系统的QPS 大约为 1200 左右,但是对应时刻的出口带宽竟然达到500MB...

Delphi 系统[28]关键字和保留字 index、near、far、export、exports、external、name、resident

Delphi 系统[28]关键字和保留字  index、near、far、export、exports、external、name、resident 1、定义: index :用于在属性中标识序号,以便用相同的属性方法(Get,Set)对不同的属性进行操作。index 关键字也用于在属性中指出多个元素。 near :标明函数的调用协定,指出函数可以被本地...

c#操作MangoDB 之MangoDB CSharp Driver驱动详解

序言MangoDB CSharp Driver是c#操作mongodb的官方驱动。 官方Api文档:http://api.mongodb.org/csharp/2.2/html/R_Project_CSharpDriverDocs.htm#! 驱动的具体介绍:https://docs.mongodb.org/ecosystem/drivers/csharp...

HBase BulkLoad批量写入数据实战

1.概述 在进行数据传输中,批量加载数据到HBase集群有多种方式,比如通过HBase API进行批量写入数据、使用Sqoop工具批量导数到HBase集群、使用MapReduce批量导入等。这些方式,在导入数据的过程中,如果数据量过大,可能耗时会比较严重或者占用HBase集群资源较多(如磁盘IO、HBase Handler数等)。今天这篇博客笔者将为大家分...

redis五大类型用法

Redis五大类型:字符串(String)、哈希/散列/字典(Hash)、列表(List)、集合(Set)、有序集合(sorted set)五种Controller:@Resource RedisTemplate<String, String> redisTemplate;总括:redisTemplate.opsForValue();//操作字...