C#的内存管理:堆栈、托管堆与指针

原创 2007年10月14日 22:56:00

在32位的Windows操作系统中,每个进程都可以使用4GB的内存,这得益于虚拟寻址技术,在这4GB的内存中存储着可执行代码、代码加载的DLL和程序运行的所有变量,在C#中,虚拟内存中有个两个存储变量的区域,一个称为堆栈,一个称为托管堆,托管堆的出现是.net不同于其他语言的地方,堆栈存储值类型数据,而托管堆存储引用类型如类、对象,并受垃圾收集器的控制和管理。在堆栈中,一旦变量超出使用范围,其使用的内存空间会被其他变量重新使用,这时其空间中存储的值将被其他变量覆盖而不复存在,但有时候我们希望这些值仍然存在,这就需要托管堆来实现。我们用几段代码来说明其工作原理,假设已经定义了一个类class1:

class1 object1;

object1=new class1();

第一句定义了一个class1的引用,实质上只是在堆栈中分配了一个4个字节的空间,它将用来存府后来实例化对象在托管堆中的地址,在windows中这需要4个字节来表示内存地址。第二句实例化object1对象,实际上是在托管堆中开僻了一个内存空间来存储类class1的一个具体对象,假设这个对象需要36个字节,那么object1指向的实际上是在托管堆一个大小为36个字节的连续内存空间开始的地址。由此也可以看出在C#编译器中为什么不允许使用未实例化的对象,因为这个对象在托管堆中还不存在。当对象不再使用时,这个被存储在堆栈中的引用变量将被删除,但是从上述机制可以看出,在托管堆中这个引用指向的对象仍然存在,其空间何时被释放取决垃圾收集器而不是引用变量失去作用域时。

在使用电脑的过程中大家可能都有过这种经验:电脑用久了以后程序运行会变得越来越慢,其中一个重要原因就是系统中存在大量内存碎片,就是因为程序反复在堆栈中创建和释入变量,久而久之可用变量在内存中将不再是连续的内存空间,为了寻址这些变量也会增加系统开销。在.net中这种情形将得到很大改善,这是因为有了垃圾收集器的工作,垃圾收集器将会压缩托管堆的内存空间,保证可用变量在一个连续的内存空间内,同时将堆栈中引用变量中的地址改为新的地址,这将会带来额外的系统开销,但是,其带来的好处将会抵消这种影响,而另外一个好处是,程序员将不再花上大量的心思在内在泄露问题上。

当然,以C#程序中不仅仅只有引用类型的变量,仍然也存在值类型和其他托管堆不能管理的对象,如果文件名柄、网络连接和数据库连接,这些变量的释放仍需要程序员通过析构函数或IDispose接口来做。

另一方面,在某些时候C#程序也需要追求速度,比如对一个含用大量成员的数组的操作,如果仍使用传统的类来操作,将不会得到很好的性能,因为数组在C#中实际是System.Array的实例,会存储在托管堆中,这将会对运算造成大量的额外的操作,因为除了垃圾收集器除了会压缩托管堆、更新引用地址、还会维护托管堆的信息列表。所幸的是C#中同样能够通过不安全代码使用C++程序员通常喜欢的方式来编码,在标记为unsafe的代码块使用指针,这和在C++中使用指针没有什么不同,变量也是存府在堆栈中,在这种情况下声明一个数组可以使用stackalloc语法,比如声明一个存储有50个double类型的数组:

double* pDouble=stackalloc double[50]

stackalloc会给pDouble数组在堆栈中分配50个double类型大小的内存空间,可以使用pDouble[0]、*(pDouble+1)这种方式操作数组,与在C++中一样,使用指针时必须知道自己在做什么,确保访问的正确的内存空间,否则将会出现无法预料的错误。

掌握托管堆、堆栈、垃圾收集器和不安全代码的工作原理和方式,将有助于你成为真正的优秀C#程序员。

.NET Framework 自动内存管理机制深入剖析 (C#分析篇)

在.NET Framework中,内存中的资源(即所有二进制信息的集合)分为"托管资源"和"非托管资源".托管资源必须接受.NET Framework的CLR(通用语言运行时)的管理(诸如内存类型安全...
  • syinter
  • syinter
  • 2002-11-21 09:50:00
  • 1361

深入理解 c#堆栈和托管堆

【转】堆栈和托管堆 c# 原文地址:http://blog.csdn.net/baoxuetianxia/archive/2008/11/04/3218913.aspx 首先堆...
  • liu_ben_qian
  • liu_ben_qian
  • 2012-08-24 14:48:58
  • 6164

线程堆栈(Thread Stack)和托管堆(Managed Heap)

内存格局通常分为四个区 全局数据区:存放全局变量,静态数据,常量 代码区:存放所有的程序代码 栈区:存放为运行而分配的局部变量,参数,返回数据,返回地址等, 堆区:即自由存储区       ...
  • xxdddail
  • xxdddail
  • 2014-07-04 17:23:55
  • 2196

C#垃圾回收和托管堆及堆栈

堆栈和托管堆: 首先堆栈和堆(托管堆)都在进程的虚拟内存中。(在32位处理器上每个进程的虚拟内存为4GB) 堆栈stack 堆栈中存储值类型。 堆栈实际上是向下填充,即由高内存地址指向低内存地址...
  • lin37985
  • lin37985
  • 2015-06-20 23:41:33
  • 836

认识托管堆

学习C#,你总会遇到“托管堆”这个术语,你知道堆是什么,但托管堆又是什么呢?为什么会有托管堆?内存管理是一件困难的事情,如果交给程序员来处理,那么可能会因为忘记释放内存而导致内存泄漏。于是,C#替我们...
  • Nagi_Way
  • Nagi_Way
  • 2017-04-13 14:44:26
  • 371

托管堆与堆栈

内存格局通常分为四个区   全局数据区:存放全局变量,静态数据,常量   代码区:存放所有的程序代码   栈区:存放为运行而分配的局部变量,参数,返回数据,返回地址等,   堆区:即自由存储区...
  • Lyncai
  • Lyncai
  • 2013-07-31 10:42:06
  • 2275

堆栈和托管堆的详解堆栈和托管堆的详解

  • 2010年12月28日 16:58
  • 19KB
  • 下载

堆栈和托管堆 c#(2)

C#中栈是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义;堆是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小 线程堆栈:简称栈 S...
  • u011926026
  • u011926026
  • 2016-05-19 15:41:46
  • 216

C#的内存管理:堆、栈、托管堆与指针

  • 2010年06月03日 13:37
  • 38KB
  • 下载

C#内存泄漏 非托管堆内存泄漏

刚进实验室,老师给我一项任务:一个已经开发好的软件,有两个BUG,要我去改。软件是用C#写的,这个以前没搞过C#。所以就一边看书学习一边看代码。第一个BUG其实严格来说不叫BUG,而是功能的完善:目前...
  • ljg888
  • ljg888
  • 2011-04-28 23:33:00
  • 5314
收藏助手
不良信息举报
您举报文章:C#的内存管理:堆栈、托管堆与指针
举报原因:
原因补充:

(最多只允许输入30个字)