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#程序员。

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

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

C#内存管理-栈堆/回收器托管/非托管资源释放/指针的应用

1.栈内存-从上往下增长,释放时从下部的末尾出栈 .net 5中栈好像也是从下往上增长了,释放时从上部顶端出栈。 栈类型数据,整体是从进程空间中的栈内存资源的最大地址处开始分配栈内存的,栈指针总是指向...
  • Blues1021
  • Blues1021
  • 2015年10月11日 12:03
  • 1273

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

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

内存管理之堆和栈的区别

堆和栈的区别 不知道谁写的,很详细,对了解程序数据存储有一定帮助,转载过来自己学习同时与众分享。 一、预备知识―程序的内存分配 一个由C/C++编译的程序占用的内存分为以下几个部分 ...
  • haiross
  • haiross
  • 2015年07月27日 09:51
  • 985

C# 深入理解堆栈、堆在内存中的实现

尽管在.NET framework下我们并不需要担心内存管理和垃圾回收(GarbageCollection),但是我们还是应该了解它们,以优化我们的应用程序。同时,还需要具备一些基础的内存管理工作机制...
  • taoerit
  • taoerit
  • 2016年12月01日 14:12
  • 5816

托管堆与堆栈

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

C# 托管内存与非托管内存之间的转换(结合Unity3d的实际开发)

1.c#的托管代码和非托管代码 c#有自己的内存回收机制,所以在c#中我们可以只new,不用关心怎样delete,c#使用gc来清理内存,这部分内存就是managed memory,大部分时候我们工...
  • leonwei
  • leonwei
  • 2014年12月15日 12:16
  • 10861

STM32内存管理以及堆和栈的理解

今天仔细读了一下内存管理的代码,然后还有看了堆栈的相关知识,把以前不太明白的一些东西想通了,写下来,方便以后查看,也想大家看了能指出哪里不对,然后修改。     ...
  • c12345423
  • c12345423
  • 2016年11月02日 09:38
  • 8007

【C#】堆、栈和堆栈的区别

导读:今天看视频,就看到了堆、栈这一块了。顿时就来劲儿了,为什么呢,...
  • u013034889
  • u013034889
  • 2014年10月24日 14:24
  • 2272

C#堆内存分配和栈内存分配

C#堆内存分配和栈内存分配   五大内存分区  在C#中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。 栈,就是那些由编译器在需要的时候分配,...
  • u010712693
  • u010712693
  • 2015年08月24日 14:46
  • 2782
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C#的内存管理:堆栈、托管堆与指针
举报原因:
原因补充:

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