【IT168 技术文档】在.NET中或许我们不用担心内存管理以及垃圾回收器(Garbage Collection GC)的问题,但是我们还是应该了解这些东东以便在必要的时候优化我们程序的性能。而且,如果对内存管理如何工作有所了解,那将有助于解释我们每个程序里的每个变量的运行规律。这篇文章主要内容是解释堆(Heap)和栈(Stack),各种变量以及这些变量到底是如何工作的。
.Net Framework 在执行代码时,有两个用来存储对象的地方,也就是堆和栈,用于帮助执行我们的代码。它们驻留在机器内存中,包含了所有我们需要实现的信息。
Stack VS Heap
栈多多少少用来负责跟踪你的代码里正在执行什么,或者说代码里的什么东东被called。而堆则或多或少用来跟踪我们的对象,或者说数据,大多数情况下都是数据啦——后头再详解。
把栈想象成堆砌起来由上到下的盒子。每次我们调用一个方法,就新加一个盒子到栈顶,我们用这种方法跟踪我们的程序在执行些什么。我们能用的,永远只是最顶上的那个盒子。当我们把最顶上这个盒子用掉了的时候,也就是方法执行完毕并返回的时候,我们就恶狠狠的把它扔掉!然后接着处理下一个盒子里的东东。而堆其实也是类似的东东,除了它的目的是用来保存信息(绝大多数情况下不是用来跟踪程序执行),因此堆里面的任何东西都不受限制的随便访问。堆就像是床上我们没空收拾的洗好的衣服一般,我们能很快的随便拿任何一件起来。而栈则跟壁橱里一堆装鞋子的盒子似的,我们得一个一个的从顶上取下来才能拿到下一双鞋子。
上图虽然并不是真正的内存中堆栈的样子,不过有助于我们理解它们的区别。
栈是“自我维护”的,意思是基本上是管理自个儿的(而不是别的地方的)内存。当顶部盒子不在使用,就扔之(就不是自家的雪了)!而堆呢,不太一样的是,必须得跟GC打交道——这东西用来保证堆是clean(没有过多垃圾内存)的。(木有人喜欢地上摆一堆脏衣服吧!臭死了!)。
What goes on the Stack and Heap?
当代码执行时,堆栈里头主要放置四种类型的东东:值类型,引用类型,指针(Pointers),以及指令(Instructions)。
值类型:
c#中,值类型继承自System.ValueType:
bool, byte, char, decimal, double, enum, float, int, long, sbyte, short, struct, uint, ulong, ushort
引用类型:
而引用类型则有:
class, interface, delegate, object, string
指针:
内存管理模型中的第三种东东是对一个类型的引用。这个引用通常就是指指针。我们不能直接使用指针,它们被CLR所管理。指针不同于引用类型。当我们说某某是引用类型时,实际上就意味着我们要通过指针去访问这个类型的值。一个指针占用内存中的一块空间,只想内存中另外一块空间。指针跟任何别的放在堆栈中的东西一样,是要占用物理空间的。它的值要么是null,要么就是内存地址。
指令:
在后续文章中再解释,稍安勿躁……
How is it decided what goes where? (Huh?)
okok,再啰嗦两句我们就可以正式摆弄我们的堆栈了。
这里有两条黄金规则:
引用类型总是保存在堆里头——够清楚了吧
值类型以及指针,总是保存在其被声明(Declared)的地方。稍微复杂一丁点儿,因为需要理解什么是“其被声明的地方”
栈,就像我们刚才提到的,负责跟踪单个线程(thread)运行到哪儿了。你不妨把其想象成thread的状态机,每个线程都有自个儿的栈。当我们的代码调某个方法时,线程开始执行JIT编译过并且保存在方法表(methodtable)中的指令集,同时,它把方法参数压入线程栈中。然后开始执行代码并访问方法里需要的、同时已经存在于线程栈顶部的变量。举个例子吧:
{
int result;
result = pValue + 5;
return result;
}