C# 后台内存管理

值数据类型

 

在进程的虚拟内存中,有一个区域称为堆栈,堆栈存储的不是对象而是值数据类型。别外在调用一个方法时,也使用堆栈存储传递给方法的所有参数的复本。为了理解堆栈的工作原理,需要注意C#中变量的作用域。 如果变量 a 在变量  b 之前作用域, b  就会先出作用域 。 代码:

 

{

     int a ;

     //  do Something;

    {

          int b;

          // do Something;

    }

}

 

首先申明 a  。在内部代码中申明 b ,然后内部的代码块终止,b 就出作用域,最后 a 出作用域,所以 b 的生存周期完全包含在 a 的生存周期内,在释放变量时,其顺序问题与给变量分配内存顺序相反,这就是堆栈的工作方式。

 

程序第一次运行时,堆栈指针指向为堆栈保留的内存块的末尾,堆栈实际上是向下填充的,即从高内存地址向低内存地址填充,当数据入栈后,指针会随即调整,以始终指向下一个自由空间。 如图  12-1 显示堆栈指针 80000 下一个自由空间地址是 79999

下面这段代码告诉编译器 ,需要一些存储单元存放一个整数和一个双精度的浮点数,这些存储单元会分配给 nRacingCards  engineSize,声明每个变量的代码表示开始请求访问这个变量,闭合花括号表示变量出作用域的地方。

 

{

    int  nRacingCards = 10;

    double  engineSize = 1000.0;

}

 

假定如图 12-1 所示  变量 a 进入作用域,赋值为10,这个值存放在存储单元799996 - 799999 这四个字节就是堆栈指针所指空间下面,(C#中 int 类型占四个字节) 为了容纳该 int ,应从堆栈指针中减去4,所以它现在指向799996 ,即下一个自由空间(799995 )。

下一行代码申明变量 engineSize ,把它初始化 1000.0, 占8个字节 ,所以在堆栈上的存储单元为 799988 - 799995,堆栈指针减去,再次指向堆栈上的下一个自由空间。

当 engineSize了作用域时,计算机就不再需要这个变量了,因为变量的生存周期总是嵌套的,所以当engineSize在作用域时,堆栈指针总是指向它的,为了删除这个变量 应给堆栈指针加8,现在指向engineSize使用过的空间,如果nRacingCards出作用域 ,堆栈指针再加4,此时如果内存中放入别一个变量 ,从799999开始的存储单元就会被覆盖,这些空间就是以前存储nRacingCards的。

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

 

引用类型

 

void DoWork()

{

    Customer arable;

    arable = new Customer();

    Customer  other=new EnhancedCustomer();

}

(假设存在类 Customer 和它的扩展类EnhancedCustomer)

 

第一句 声明一个Customer 引用 arable,在堆栈上分配给这个引用存储空间,它不是对象,占4个字节,存放Customer对象的地址

arable = new Customer();  这段代码运行后,首先 ,在堆上分配内存,以存储Customer实例,然后把arable的值设置为分配给Customer对象的内存地址, Customer实例没有放在堆栈 中,而是放在内存的堆中,(假定 Customer对象点32个字节) 为了在堆上找到一个新的存储空间,.NET运行库在堆上搜索,找到第一个连续的、未使用的32字节的内存块, 假这其地址是200000。 给Customer对象分配过空间后,内存内容应如图 12-3 ,注意,与堆栈不同堆上的内存是向上分配的,所以自由空间在已用空间的上面

 

从这个例子可心看出,建立引用变量的过程要比建立变量的过程更复杂,且不能避免性能的降低。实际上,我们对这个过程进行了过分的简化,因为.net运行库需要保存堆的状态信息,在堆中添加新数据时,这些信息也需要更新。尽管有这些性能的损失,但仍有一种机制,在给变量分配内存时,不会受到堆栈 的限制。把一个引用变量的值赋予别一个相同类型的变量,就有两个引用内存中同一个对象的变量 了。当一个引用变量出作用域时,它会从堆栈中删除,如上一节所述,但引用对象的数据仍保留在堆中,一直到程序停止,或垃圾收集器删除它为止,而只有在该数据不再被任何变量引用是,才会被删除。

 

 

垃圾收集

 

在垃圾收集运行时,会在堆上删除不再引用的对象 ,在完成删除操作后,堆会立即分散开来,与已经释放的内存混合在一起,如图 12-4  

如果托管的堆也是这样,在其上给新的对象分配内存就成为一个很难处理的过程,运行库必须搜索整个堆,才能找到足够大的内存块来存放每个新对象。但是,垃圾收集器不会让堆处于这种状态。只要它释放了能释放的所有对象,就会把其它对象移动回堆的端部,再次形成一个连续的块。因此,堆可以继续像堆栈那亲确定在什么地方存储新对象。当然,在移动对象 时,这些对象的所有引用都需要用正确的新地址来更,但垃圾收集器也会处理更新问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值