不同于其他语言的地方,堆栈存储值类型数据,而托管堆存储引用类型如类、对象,并受垃
圾收集器的控制和管理。在堆栈中,一旦变量超出使用范围,其使用的内存空间会被其他变量重新使用,这时其空间中存储的
值将被其他变量覆盖而不复存在,但有时候我们希望这些值仍然存在,这就需要托管堆来实现。我们用几段代码来说明其工作
原理,假设已经定义了一个类
第一句定义了一个 的引用,实质上只是在堆栈中分配了一个 个字节的空间,它将用来存府后来实例化对象在托
管堆中的地址,在 中这需要 个字节来表示内存地址。第二句实例化 对象,实际上是在托管堆中开僻了一
个内存空间来存储类 的一个具体对象,假设这个对象需要 个字节,那么 指向的实际上是在托管堆一个大小
为 个字节的连续内存空间开始的地址。由此也可以看出在 编译器中为什么不允许使用未实例化的对象,因为这个对象
在托管堆中还不存在。当对象不再使用时,这个被存储在堆栈中的引用变量将被删除,但是从上述机制可以看出,在托管堆中
这个引用指向的对象仍然存在,其空间何时被释放取决垃圾收集器而不是引用变量失去作用域时。
在使用电脑的过程中大家可能都有过这种经验:电脑用久了以后程序运行会变得越来越慢,其中一个重要原因就是系统中
存在大量内存碎片,就是因为程序反复在堆栈中创建和释入变量,久而久之可用变量在内存中将不再是连续的内存空间,为了
寻址这些变量也会增加系统开销。在 中这种情形将得到很大改善,这是因为有了垃圾收集器的工作,垃圾收集器将会压缩
托管堆的内存空间,保证可用变量在一个连续的内存空间内,同时将堆栈中引用变量中的地址改为新的地址,这将会带来额外
的系统开销,但是,其带来的好处将会抵消这种影响,而另外一个好处是,程序员将不再花上大量的心思在内在泄露问题上。
当然,以 程序中不仅仅只有引用类型的变量,仍然也存在值类型和其他托管堆不能管理的对象,如果文件名柄、网络
连接和数据库连接,这些变量的释放仍需要程序员通过析构函数或 !" 接口来做。
另一方面,在某些时候 程序也需要追求速度,比如对一个含用大量成员的数组的操作,如果仍使用传统的类来操作,
将不会得到很好的性能,因为数组在 中实际是 #$% &''$ 的实例,会存储在托管堆中,这将会对运算造成大量的额外
的操作,因为除了垃圾收集器除了会压缩托管堆、更新引用地址、还会维护托管堆的信息列表。所幸的是 中同样能够通过
不安全代码使用 ((程序员通常喜欢的方式来编码,在标记为 )* 的代码块使用指针,这和在 ((中使用指针没有什么
不同,变量也是存府在堆栈中,在这种情况下声明一个数组可以使用 + 语法,比如声明一个存储有 ,- 个 ) 类
型的数组:
).")+)/,-0
+ 会给 ") 数组在堆栈中分配 ,- 个 ) 类型大小的内存空间,可以使用 ")/-0、.")(这
种方式操作数组,与在 ((中一样,使用指针时必须知道自己在做什么,确保访问的正确的内存空间,否则将会出现无法预
料的错误。
掌握托管堆、堆栈、垃圾收集器和不安全代码的工作原理和方式,将有助于你成为真正的优秀 程序员。
进程中每个线程都有自己的堆栈,这是一段线程创建时保留下的地址区域。我们的“栈内存”即在此。至于“堆”内存,我个人
认为在未用 定义时,堆应该就是未“保留”未“提交”的自由空间, 的功能是在这些自由空间中保留(并提交?)出一个
地址范围