由浅入深CIL系列:3.通过CIL观察.NET值类型和引用类型的内存分配

        一、在.NET中,内存分配是非常重要的一大块,为了更深入的了解其分配情况,本节中我们将利用一个实例来查看其CIL语言分析内存的分配情况。下面我们首先来看实例C#源码如下:
 
  
class Program
{
static void Main( string [] args)
{
// 将a+b+c,打印结果
int a = 3 ;
int b = 19 ;
double c = 443.25 ;
Console.WriteLine(a
+ b + c);

// 分别打印d,e,d+e
string d = " Hello World! " ;
string e = " Print Word! " ;
Console.WriteLine(e);
Console.WriteLine(d);
Console.WriteLine(d
+ e);
}
}
        二、接下来我们看这段程序的CIL代码,通过这段代码我们大概能够猜出分别代表了什么意思。
 
  
.method private hidebysig static void Main( string [] args) cil managed
{
// 第一段声明
.entrypoint
// 代码大小 71 (0x47)
.maxstack 2
.locals init ([
0 ] int32 a,
[
1 ] int32 b,
[
2 ] float64 c,
[
3 ] string d,
[
4 ] string e)
// 第二段值类型内存存储情况
IL_0000: nop
IL_0001: ldc.i4.
3
IL_0002: stloc.
0
IL_0003: ldc.i4.s
19
IL_0005: stloc.
1
IL_0006: ldc.r8
443.25
IL_000f: stloc.
2
IL_0010: ldloc.
0
IL_0011: ldloc.
1
IL_0012: add
IL_0013: conv.r8
IL_0014: ldloc.
2
IL_0015: add
IL_0016: call
void [mscorlib]System.Console::WriteLine(float64)
// 第三段引用类型内存存储情况
IL_001b: nop
IL_001c: ldstr
" Hello World! "
IL_0021: stloc.
3
IL_0022: ldstr
" Print Word! "
IL_0027: stloc.s e
IL_0029: ldloc.s e
IL_002b: call
void [mscorlib]System.Console::WriteLine( string )
IL_0030: nop
IL_0031: ldloc.
3
IL_0032: call
void [mscorlib]System.Console::WriteLine( string )
IL_0037: nop
IL_0038: ldloc.
3
IL_0039: ldloc.s e
IL_003b: call
string [mscorlib]System.String::Concat( string ,
string )
IL_0040: call
void [mscorlib]System.Console::WriteLine( string )
IL_0045: nop
IL_0046: ret
}
// end of method Program::Main

        首先我们看第一段CIL代码所示,声明了程序的进入点,以及定义了5个局部的变量其索引值分别为0,1,2,3,4,变量名为a,b,c,d,e。

 
  
.entrypoint // 定义了程序的进入点
// 代码大小 71 (0x47) // 表明代码总共大小71个字节
.maxstack 2
.locals init ([
0 ] int32 a, // 在索引的0,1,2,3,4处定义了5个局部变量
[ 1 ] int32 b,
[
2 ] float64 c,
[
3 ] string d,
[
4 ] string e)

         其次我们来看第二段CIL代码,这是值类型的直接存储在栈中的数据,直接取出相加即可。

 
  
// 第二段值类型内存存储情况
IL_0000: nop
// int a = 3;
// 将整数值 3 作为 int32 推送到计算堆栈上
IL_0001: ldc.i4. 3
// 将堆栈顶部的3弹出并且存储到索引为1处得局部变量b
IL_0002: stloc. 0
// int b = 19;
// 将整数19作为int32推送到计算机堆栈上
IL_0003: ldc.i4.s 19
// 将堆栈顶部的19弹出并且存储到索引为1处得局部变量b
IL_0005: stloc. 1
// double c = 443.25;
// 将所提供的 float64 类型的值443.25作为 F (float) 类型推送到计算堆栈上
// 从这里可以看出C# Double类型==MSIL里面的float64类型
IL_0006: ldc.r8 443.25
// 将堆栈顶部的443.25弹出并且存储到索引为2处得局部变量b
IL_000f: stloc. 2
// a+b
// 将索引 0 处的局部变量a值3加载到计算堆栈上
IL_0010: ldloc. 0
// 将索引 1 处的局部变量b值19加载到计算堆栈上
IL_0011: ldloc. 1
// 将两个值相加并将结果推送到计算堆栈上,结果为22
IL_0012: add
// a+b得到的值22+c
// 将位于计算堆栈顶部的值22转换为 float64
IL_0013: conv.r8
// 将索引 2 处的局部变量b值443.25加载到计算堆栈上
IL_0014: ldloc. 2
// 将两个值相加并将结果465.25推送到计算堆栈上
IL_0015: add
// 调用mscorlib程序集内的函数打印值465.25
IL_0016: call void [mscorlib]System.Console::WriteLine(float64)

启发:

1.在.NET的CIL语言中首先建立一个变量的索引集合。然后在每次初始化值类型的时候先将值类型的值推到计算堆栈上,然后马上将堆栈上顶部的对应值存到对应的索引项中。

2.值类型并没有入堆,而是直接在栈上使用。

3.C#中的Double类型在CIL中实质上就是float64类型,且int32类型和float64类型一起做运算的时候,需要先将int32类型转为float64类型。

        再次我们看以下代码,以观察引用类型在内存中的存储方式和使用方法。

 
  
// 第三段引用类型内存存储情况
IL_001b: nop
// 为Hello World!字符串分配内存,并且推送其对象引用到计算堆栈上。
IL_001c: ldstr " Hello World! "
// 将堆栈顶部的Hello World!字符串的引用弹出并且存储到索引为3处得局部变量d
IL_0021: stloc. 3
// 为Print Word!字符串分配内存,并且推送其对象引用到计算堆栈上。
IL_0022: ldstr " Print Word! "
// 将堆栈顶部的Print Word!字符的引用弹出并且存储到索引为4处的局部变量e
IL_0027: stloc.s e
// 将索引 4 处的局部变量Print Word!字符串加载到计算堆栈上并且打印出来
IL_0029: ldloc.s e
IL_002b: call
void [mscorlib]System.Console::WriteLine( string )

IL_0030: nop
// 将索引 3 处的局部变量Hello World!字符串加载到计算堆栈上并且打印出来
IL_0031: ldloc. 3
IL_0032: call
void [mscorlib]System.Console::WriteLine( string )
IL_0037: nop
// 将d和e的字符串提取出来并且调用[mscorlib]System.String::Concat(string,string)
// 对两个string进行值拷贝相加,然后将其引用存到栈顶
IL_0038: ldloc. 3
IL_0039: ldloc.s e
IL_003b: call
string [mscorlib]System.String::Concat( string ,
string )
// 将存在栈顶的新的string弹出显示出来
IL_0040: call void [mscorlib]System.Console::WriteLine( string )
IL_0045: nop
IL_0046: ret

启发:

1.string是一种特殊的引用类型。

2.string类型的字符相加操作是使用[mscorlib]System.String::Concat(string,string)对值的拷贝实现的。

3.引用类型是先将其值在内存堆中分配好之后才返回其引用到栈上面。

        本节通过CIL语言观察到.NET的值类型和引用类型在内存堆和栈中是如何进行访问和管理的。以及特殊的string引用类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值