c#内存管理和指针
2007-11-17 21:38
351 查看
using aliasname = NamaSpaceName;
#define DEBUG
#undef DEBUG #warning #error #line #pragma 抑制恢复警告
ref 和out 的区别
ref在传递参数的时候要求初始化,而out不需要初始化.
七 内存管理和指针
4GB内存中的每个存储单元都是从0开始往上排序的,要访问存储在内存的某个空间中的一个值,就需
要提供表示该存储单元的数字.在任何复杂的高级语言中,例如C#、vb、C++和Java中编译器复杂把人们
可以理解的名称转换为处理器可以理解的内存地址。
在进程的虚拟内存中,有一个区域称为堆栈,堆栈存储不是对象成员的值数据类型。另外,在调用
一个方法时,也使用堆栈复制传递给方法的所参数。为了理解堆栈的工作原理, 需要注意在C#中变量
的作用域。如果变量a在变量b之前进入作用域,b就会先出作用域。就是先声名后释放。
我们不知道堆栈在地址空间的什么地方,这些信息在进行C#开发式不需要知道的。堆栈指针(操作
系统维护的一个变量)表示堆栈中下一个自由空间的地址。程序第一次运行时,堆栈指向为堆栈保留
的内存块末尾。堆栈实际是向下填充的,即从高内存地址向低内存地址填充。当数据入栈后,堆栈指
针就会随之调整,以始终指向下一个自由空间。这种情况如图所示。在该图中,显示了堆栈指针
800000(16进制的0xC3500),下一个自由空间是地址799999。
如果编译器遇到像int i,这样的代码,则这两个变量进入作用域的顺序就是不确定的、两个变量
是同时声明的,也是同时处作用域的。此时,变量以什么顺序从内存中删除就不重要了,编译器在内
部会确保先放在内存中的那个变量后删除,这样就能保证该规则不会与变量的生存期冲突。
但是变量的生存期必须嵌套,在托管堆中存储的数据就有这种可能,托管堆是进程的可用4GB中的
另一个内存区域。堆上得内存是向上分配的,所以自由空间在已用空间的上面。
我们对这个过程进行了过分的简化,因为.net运行库需要保存堆的状态信息,在堆中添加新数据中
,这些信息也需要更新.尽管有这些性能损失,但仍有一种机制,在给变量分配内存时,不会受到堆栈的限
制.把一个引用变量的值赋于另一个相同类型的变量,
就有两个引用内存中同一个对象的变量了.当一个引用变量出作用域时,它会从堆栈中删除,但引用对象
的数据仍保留在堆中,一直到程序停止,或垃圾收集器删除它为止,而只有在该数据不再被任何变量引用
时,才会被删除.
托管堆的工作方式非常类似于堆栈,在某种程度上,连续的对象会在内存中一个挨一个地放置,这样
就很容易使用指向下一个空闲存储单元的堆指针,来确定下一个对象的位置.在堆上添加更多的对象时,
也容易调整,但这比较复杂,因为基于堆对象的生存期与引用他们的基于堆栈的变量的作用域不匹配.
在垃圾收集器运行时,会在堆中删除不再引用的所有对象.在完成删除动作后,堆会立即把对象分散开
来,与已经释放的内存混合在一起.
如果托管的堆也是这样,在其上给新对象分配内存就成为一个很难处理的过程,运行库必须搜索整
个堆,才能找到足够大的内存块来存储每个新对象.但是,垃圾收集器不会让堆处于这种状态.只要它释
放了能释放的所有对象,就会压缩其他对象,把它们都移动回堆的端部.再次形成一个连续的块,因此,对
于在什么地方存储新对象,堆可以继续像堆栈那样工作.当然,在移动对象,这些对象的所有引用都需要
用正确的新地址来更新,但垃圾收集器也会处理更新问题.
垃圾收集器的这个压缩操作是托管的堆与旧未托管得堆的区别所在.使用托管的堆,就只需要读取
堆指针的值即可,而不是搜索链接地址列表,来查找一个地方来放置新数据.因此在.NET下实例化对象要
快得多,有趣的是,访问它们也比较快,因为对象会压缩到堆上相同的内存区域,这样需要交换的页面较
少.Microsoft相信,尽管垃圾收集器需要作一些工作,修改它移动的所有对象引用,致使性能降低,但这
些性能会得到弥补.
#define DEBUG
#undef DEBUG #warning #error #line #pragma 抑制恢复警告
ref 和out 的区别
ref在传递参数的时候要求初始化,而out不需要初始化.
七 内存管理和指针
4GB内存中的每个存储单元都是从0开始往上排序的,要访问存储在内存的某个空间中的一个值,就需
要提供表示该存储单元的数字.在任何复杂的高级语言中,例如C#、vb、C++和Java中编译器复杂把人们
可以理解的名称转换为处理器可以理解的内存地址。
在进程的虚拟内存中,有一个区域称为堆栈,堆栈存储不是对象成员的值数据类型。另外,在调用
一个方法时,也使用堆栈复制传递给方法的所参数。为了理解堆栈的工作原理, 需要注意在C#中变量
的作用域。如果变量a在变量b之前进入作用域,b就会先出作用域。就是先声名后释放。
我们不知道堆栈在地址空间的什么地方,这些信息在进行C#开发式不需要知道的。堆栈指针(操作
系统维护的一个变量)表示堆栈中下一个自由空间的地址。程序第一次运行时,堆栈指向为堆栈保留
的内存块末尾。堆栈实际是向下填充的,即从高内存地址向低内存地址填充。当数据入栈后,堆栈指
针就会随之调整,以始终指向下一个自由空间。这种情况如图所示。在该图中,显示了堆栈指针
800000(16进制的0xC3500),下一个自由空间是地址799999。
如果编译器遇到像int i,这样的代码,则这两个变量进入作用域的顺序就是不确定的、两个变量
是同时声明的,也是同时处作用域的。此时,变量以什么顺序从内存中删除就不重要了,编译器在内
部会确保先放在内存中的那个变量后删除,这样就能保证该规则不会与变量的生存期冲突。
但是变量的生存期必须嵌套,在托管堆中存储的数据就有这种可能,托管堆是进程的可用4GB中的
另一个内存区域。堆上得内存是向上分配的,所以自由空间在已用空间的上面。
我们对这个过程进行了过分的简化,因为.net运行库需要保存堆的状态信息,在堆中添加新数据中
,这些信息也需要更新.尽管有这些性能损失,但仍有一种机制,在给变量分配内存时,不会受到堆栈的限
制.把一个引用变量的值赋于另一个相同类型的变量,
就有两个引用内存中同一个对象的变量了.当一个引用变量出作用域时,它会从堆栈中删除,但引用对象
的数据仍保留在堆中,一直到程序停止,或垃圾收集器删除它为止,而只有在该数据不再被任何变量引用
时,才会被删除.
托管堆的工作方式非常类似于堆栈,在某种程度上,连续的对象会在内存中一个挨一个地放置,这样
就很容易使用指向下一个空闲存储单元的堆指针,来确定下一个对象的位置.在堆上添加更多的对象时,
也容易调整,但这比较复杂,因为基于堆对象的生存期与引用他们的基于堆栈的变量的作用域不匹配.
在垃圾收集器运行时,会在堆中删除不再引用的所有对象.在完成删除动作后,堆会立即把对象分散开
来,与已经释放的内存混合在一起.
如果托管的堆也是这样,在其上给新对象分配内存就成为一个很难处理的过程,运行库必须搜索整
个堆,才能找到足够大的内存块来存储每个新对象.但是,垃圾收集器不会让堆处于这种状态.只要它释
放了能释放的所有对象,就会压缩其他对象,把它们都移动回堆的端部.再次形成一个连续的块,因此,对
于在什么地方存储新对象,堆可以继续像堆栈那样工作.当然,在移动对象,这些对象的所有引用都需要
用正确的新地址来更新,但垃圾收集器也会处理更新问题.
垃圾收集器的这个压缩操作是托管的堆与旧未托管得堆的区别所在.使用托管的堆,就只需要读取
堆指针的值即可,而不是搜索链接地址列表,来查找一个地方来放置新数据.因此在.NET下实例化对象要
快得多,有趣的是,访问它们也比较快,因为对象会压缩到堆上相同的内存区域,这样需要交换的页面较
少.Microsoft相信,尽管垃圾收集器需要作一些工作,修改它移动的所有对象引用,致使性能降低,但这
些性能会得到弥补.
相关文章推荐
- 【转】C#的内存管理:堆栈、托管堆与指针
- C#2005高级编程Chapter11内存管理和指针学习笔记
- C#的内存管理:堆栈、托管堆与指针 (转)
- C#的内存管理:堆栈、托管堆与指针
- C#的内存管理:堆栈、托管堆与指针
- C#的内存管理:堆、栈、托管堆与指针
- 【转】C#的内存管理:堆栈、托管堆与指针
- C#的内存管理:堆、栈、托管堆与指针(助于理解 new struct using)
- C#的内存管理:堆、栈、托管堆与指针
- C#的内存管理:堆栈、托管堆与指针
- C# 内存管理和指针
- C#的内存管理:堆栈、托管堆与指针
- C#的内存管理:堆栈、托管堆与指针
- C#的内存管理:堆栈、托管堆与指针 (转)
- [转]C#的内存管理:堆栈、托管堆与指针
- C#的内存管理:堆、栈、托管堆与指针[转]
- [2011-11-23] 教训:C++的DLL和C#采用不同的内存管理,不要返回指向堆的指针,而改成读写文件
- C#的内存管理:堆栈、托管堆与指针
- C#的内存管理:堆栈、托管堆与指针 (转)
- C#2005高级编程11Chapter内存管理和指针