之前一直被灌输,C#分值类型和引用类型,在程序运行时,它们分别存在栈(Stack) 和堆(Heap)上。这也是面试经典问题了,但其实其中存在很大的误解。比如某个实例对象中有一个Int型成员。当这个实例初始化并被赋值后,该成员是在 堆中还是栈中?如果始终在栈中显然无法解释,因为栈中数据离开作用域就被释放了。
真实的概念应该是,.net在运行中需要4类数据,值类型,引用类型,指向对象实例(即引用类型)的指针,以及指令(方法)。这些东西中除引用类型外,在执行某个方法时会按顺序放入栈中,而方法结束退出时即离开作用域时,会自动从栈上释放相关数据。
这里有一篇文章详细介绍了.net如何在执行方法时使用栈和堆,相当详细:
http://www.cnblogs.com/c2303191/articles/1065675.html
所以最终的解释是,一个程序,启动时就相当于启动了一个main函数的方法(Web程序由IIS host,本质上是一样的)。在运行时实例化的引用类型及该实例内部的成员数据其实都是放在堆上的,但执行中具体要被调用的变量,方法参数,指针,指令则 会放入栈中使用,目的是满足值类型的隔离性以及在脱离作用域时自动释放。
那为何需要分为栈和堆呢?能否相互替代呢?答案显然是不能的。
栈是在某个应用程序(进程)启动前就被分配好最大容量的,放入栈的内容都必须是确定其大小的,所以只能是值类型,指针或指令。没有堆的配合无法进行面向对象的编程。
只有堆的话虽然什么都可以放,但是也有很多缺陷,主要有3点:
1.栈分配内存比堆快:栈中存入的数据大小是确定的,只需要简单的地址偏移即可。
2.堆不会靠内存机制自动释放,容易产生内存泄露。
3.堆会产生内存碎片。
针对2和3,.net靠垃圾回收(GC)来自动释放托管对象以及压缩内存碎片,但频繁GC会导致程序性能很差,开销很大。
所以只有当栈和堆互补时,.net的性能和安全性才是最优的。