C# 内存管理篇---值数据类型

Windows 使用一个虚拟寻址系统, 该系统把程序可用的内存地址映射到硬件内存中的实际地址上,这些任务完全由Windows在后台管理。 其实际结果是32位处理器上的每个进程都可以使用4GB的内存, 无论计算机上实际有多少物理内存(64位处理器上,这个数字会更大)。在这个4GB的内存实际上包含了程序的所有部分,包括可执行代码、代码加载的所有DLL, 以及程序运行时使用的所有变量的内容。 这个4GB的内存称为虚拟地址空间, 或虚拟内存。 为了方便起见,本章将它简称为内存

在4GB中每个存储单元都是从0开始往上排序的。要访问存储在内存的某个空间中的一个值,就需要提供表示该存储单元的数字。在任何复杂的高级语言中,编译器负责把人们可以理解的变量转换为处理器可以理解的内存地址。

在处理器的虚拟内存中,有一个区域称为为了理解栈的工作原理, 需要注意在C#中的变量作用域

{
    int a;
    
    {
        int b;
    }
}

看上面的代码,首先声明了变量a。 接着在内存代码块中声明了b。 然后内部代码块终止, b就超出作用域, 最后a超出作用域。所以b的生存期是完全包含在a的生存期中的。 在释放变量时,其顺序总是与给它们分配内存的顺序相反,这就是栈的工作方式。

还要注意,b在另一个代码块中(通过另一对嵌套的花括号来定义)。因此,它包含在另一个作用域中。 这称为块作用域或结构作用域。

我们不知道栈具体在地址空间的什么地方, 这些信息在进行C#开发时是不需要知道的。 栈指针(操作系统维护的一个变量)表示栈中下一个空闲存储单元的地址。程序第一次开始运行时,栈指针指向为栈保留的内存块末尾。栈实际上是向下填充的,既从高内存地址向低内存地址填充。当数据入栈后,栈指针就会随之调整,始终保持指向下一个空闲存储单元。

我们看下上面这张图, 在该图中,显示了栈指针指向 800000(十六进制的 0xC3500), 下一个空闲存储单元是地址 799999。

下面的代码会告诉编译器,需要一些存储空间来存储一个整数和一个双精度浮点数。

{
    int a = 10;
    double b = 3000.0;
}

假设 栈指针目前指向的是 800000, 当 a变量进入作用域,赋值为 10, 这个值放在存储单元 799996~799999上,这4个字节就在栈指针所指空间的下面。 有4个字节是因为存储 int 要使用4个字节。 为了容纳 int , 应从栈指针对应的值中减去 4,所以它现在指向的位置应该是 799996, 即下一个空闲单元 (799995)。

然后声明的 b变量也进入了作用域, 赋值为 3000.0。一个double需要8个字节, 所以放在栈上的存储单元应该是 799988~799995上,栈指针对应的减去8, 再次指向栈上的下一个空闲单元。

当 b 超出作用域时, 运行库就知道不再需要这个变量了。 因为变量的生存期总是嵌套的, 当 b 在作用域时,无论发生什么情况,都可以保证栈指针总是会指向存储 b 的空间。为了从内存中删除这个变量,应给栈指针对应的值加8 。 当a 也超出作用域栈指针对应的值就再次加4。从栈中删除 b 和 a 之后, 此时如果在作用域中又放入另一个变量, 从 799999 开始的存储单元就会被覆盖, 这些空间之前是存储 a 的。

如果编译器遇到 int i,j; 这样的代码行, 则这两个变量进入作用域的顺序是不确定的。两个变量是同时声明的,也是同时超出作用域的 ,因此变量以什么样的顺序从内存中删除就不重要了。 编译器在内部会确保先放在内存中的那个变量后删除,这样就能保证该规则不会与变量的生存期冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值