C#编程的一个优点是程序员不需要担心具体的内存管理,垃圾回收器会自动处理所有的垃圾回收工作。用户可以得到像C++语言那样的效率,而不需要考虑像在C++中那样内存管理工作的复杂性。虽然不必手动管理内存,但仍需要理解后台发生的事情。
理解程序在后台如何处理内存有助于提高应用程序的速度和性能。
值数据类型
在进程的虚拟内存中,有一个区域称为栈。栈存储不是对象成员的值数据类型。另外,在调用一个方法时,也使用栈存储传递给方法的所有参数的副本。为了理解栈的工作原理,需要注意在C#中变量的作用域。如果变量a在变量b之前进入作用域,b就会首先超出作用域。下面的代码段:
{
int a;
//do something
{
int b;
//do something else
}
}
首先声明a。在内部代码块中声明了b。然后内部代码块终止,b就超出作用域,最后a超出作用域。所以b的生存期完全包含在a的生存期中。在释放变量时,其顺序总是与给它们分配内存的顺序相反,这就是栈的工作方式。简言之,即:先进后出,后进先出。
引用数据类型
托管堆(简称堆)是处理器的可用4GB中的另一个内存区域。要了解堆的工作原理和如何为引用数据类型分配内存,看看下面的代码:
void DoWork()
{
Customer arable;
arabel = new Customer();
Customer otherCustomer2 = new EnhancedCustomer();
}
在这段代码中,假定存在两个类Customer 和 EnhancedCustomer。EnhancedCustomer类扩展了Customer类。
首先,声明一个Customer引用 arable,在栈上给这个引用分配存储空间,但这仅是一个引用,而不是实际的Customer对象。arable引用占用4个字节的空间,足够包含 在其中存储Customer对象的地址。
arabel = new Customer();
这行代码完成了以下操作:首先,它分配堆上的内存,以存储Customer对象(一个真正的对象,而不只是一个地址)。然后把变量arabel的值设置为分配给新Customer对象的内存地址。
Customer实例没有放在栈中,而是放在堆中。在这个例子中,
从上面两个例子中能够看出,建立引用变量的过程要比建立值变量的过程更复杂,且不能避免性能的系统开销。
当一个引用变量超出作用域时,它会从栈中删除,但引用对象的数据仍然保留在堆中,一直到程序终止,或者垃圾回收器删除它为止,而只有在该数据不再被任何变量引用时,它才会被删除。