`
lovnet
  • 浏览: 6880781 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

C#中的堆与栈

 
阅读更多


在看楚广明老师讲的C#视频时,里面提到了“堆与栈[zhàn]”,很是一头雾水。所以就动手研究了一下。下面是研究的结果,如果和真正的理论意义有出入,还请见谅!


一、堆与栈的定义

堆(heap):

堆是从下往上分配,所以已用的空间在自由空间下面,C#中所有引用类型的对象分配在托管堆上,托管堆在内存上是连续分配的,并且内存对象的释放受垃圾收集机制的管理,效率相对于栈来说要低的多。

栈(stack):

栈是自上向下进行填充,即由高内存地址指向低内存地址,并且内存分配是连续的,C#中所有的值类型和引用类型的引用都分配在栈上,栈根据后进先出的原则,依次对分配和释放内存对象。


下面解释几个关于“堆与栈“方面的名词:


1、栈区(stack由编译器自动分配释放,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2
、堆区(heap一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3
、全局区(静态区)(static,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区。程序结束后由系统释放。
4
、文字常量区常量字符串就是放在这里的。程序结束后由系统释放
5
、程序代码区存放函数体的二进制代码。


理解堆与栈对于理解.NET中的内存管理、垃圾回收、错误和异常、调试与日志有很大的帮助。垃圾回收的机制使程序员从复杂的内存管理中解脱出来,虽然绝大多数的C#程序并不需要程序员手动管理内存,但这并不代表程序员就无需了解分配的对象是如何被回收的,在一些特殊的场合仍需要程序员手动进行内存管理。


C#中对象内存的分配与销毁:

当一个类的实例对象创建的时候,这个对象的不同成员按类别被分配到了不同的内存区域,值类型和引用类型的指针被分配到了栈上,引用类型的实例对象被分配到了托管堆上,静态成员被分配到了全局数据区。此时栈上的指针会指向堆上的对象。当对象使用完以后,引用和实际对象的联系就会断开,从而从而使对象冬眠。因为栈具有自我维护性,它的内存管理可以通过操作系统来完成,而此时堆上的冬眠对象就需要通过垃圾回收器(GC)使用一定的算法来进行回收,释放对象所占据的内存。

C#中的深拷贝与浅拷贝:

深拷贝:又称深度克隆,它完全是新对象的产生,不仅复制所有的非静态值类型成员,而且复制所有引用类型成员的实际对象。(即栈上和堆上的成员均进行复制)

浅拷贝:又称影子克隆,只复制原始对象中的所有的非静态的值类型成员和所有引用类型成员的引用,就是说,原始对象和新对象共享所有引用类型成员的对象实例。(即只复制栈上的成员)

(注:不管是深拷贝还是浅拷贝,都不会复制全局数据区的成员,因为全局数据区的成员是静态成员,它属于某一个类,并不属于类的实例对象,因此无法复制。)

C#中的深拷贝可以通过实现ICloneable接口来实现,但是在不是必须实现ICloneable接口的情况下,应避免类型继承ICloneable接口。因为这样做将强制所有的子类必须实现ICloneable接口,否则子类的新成员将不能被类型的深拷贝所覆盖。


堆与栈的联系:

都是用来处理变量的内存分配的。


下面主要讲一讲堆与栈的区别:


堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。


堆与栈主要有以下区别:

1、管理方式不同;

2、空间大小不同;

3、能否产生碎片不同;

4、生长方向不同;

5、分配方式不同;

6、分配效率不同;


管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak

空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M。当然,我们可以修改:

打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。

(注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。)

碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。

生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈、出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C#函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。

虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

无论是堆还是栈,都要防止越界现象的发生,因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难。

分享到:
评论

相关推荐

    C#中堆和栈的区别分析

    C#中堆和栈的区别分析 C# 中堆和栈的区别分析是理解 .NET 框架中内存管理的关键。堆和栈是两个基本元素,组成我们 C# 程序的运行环境。在这个知识点中,我们将详细介绍堆和栈的概念、类型变量、分配机制、生命周期...

    C# 堆和栈的比较,提高代码性能必备

    在.NET Framework中,C#编程语言的内存管理主要涉及两个关键概念:堆(Heap)和栈(Stack)。了解它们之间的差异对于优化代码性能至关重要。堆和栈各自承担着不同的职责,它们共同协作以确保程序的正常运行。 首先...

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

    C#的内存管理:堆、栈、托管堆与指针 C#的内存管理机制是通过堆栈、托管堆和垃圾收集器来实现的。在 32 位的 Windows 操作系统中,每个进程都可以使用 4GB 的内存,这得益于虚拟寻址技术。在这 4GB 的内存中存储着...

    堆与栈的区别

    在编程语言中,堆和栈是两种不同的内存管理机制,它们各自有特定的用途和特点。以下是关于堆和栈的详细区别和相关知识点: 1. **栈(Stack)**: - 栈是一种线性数据结构,具有后进先出(LIFO)的特点。 - 在C#和...

    浅谈C#中堆和栈的区别(附上图解)

    在C#编程中,了解堆和栈的区别是优化程序性能和理解内存管理的关键。栈和堆是两种不同的内存区域,它们各自有不同的特点和用途。 首先,栈(Stack)是编译时期就已经分配好的内存空间。栈上的内存由系统自动管理,...

    C# 内存队栈学习例子

    在C#中,内存分为堆(Heap)和栈(Stack)。栈主要存储基本类型(如int、bool、char)和引用类型(如类实例)的引用。而堆则存储对象实例,包括自定义类型和.NET框架提供的类型。 1. **栈(Stack)**: 栈是一种...

    .netC#笔试题

    - C#中堆与栈的区别:堆主要存储动态分配的对象,它的内存由垃圾回收机制管理,分配和释放相对较慢;而栈则存储基本类型变量和对象引用,内存由编译器自动管理,分配和释放快速。栈内存有限,堆内存较大。栈中的...

    .NET_C#_栈_堆_垃圾回收GC

    #### 三、栈与堆中的数据类型 - **值类型**(如整型、浮点型、结构体等):直接存储在栈中,占用固定大小的空间。当值类型变量赋值给其他变量时,实际上是复制了一个副本,而不是引用同一个对象。 - **引用类型**...

    堆和栈详细,编程必知

    栈上的内存管理与C相同。 3. C#:C#使用垃圾回收(Garbage Collection, GC)机制自动管理堆内存,程序员无需显式释放内存。局部变量和方法参数仍然在栈上分配,而类实例默认在堆上创建。C#还提供了一些高级数据结构...

    C#栈和堆的区别浅谈

    理解堆与栈对于理解.NET中的内存管理、垃圾回收、错误和异常、调试与日志有很大的帮助。垃圾回收的机制使程序员从复杂的内存管理中解脱出来,虽然绝大多数的C#程序并不需要程序员手动管理内存,但这并不代表程序员就...

    中国科学院C#课件、堆与拷贝构造函数

    4. 使用栈与堆的权衡:讨论何时应该将对象存储在栈上,何时应该存储在堆上,以及这样做的优缺点。 5. 防止内存泄漏:通过合理使用拷贝构造函数和析构函数防止内存泄漏。 6. 避免拷贝构造函数引发的问题:例如,如何...

    DataAndAlgorithm_C#数据结构与算法_栈和队列_

    本资源"DataAndAlgorithm_C#数据结构与算法_栈和队列_"专注于这两种基础但关键的数据组织方式:栈和队列,以及它们在C#语言中的实现。 栈是一种后进先出(LIFO,Last In First Out)的数据结构,常被比喻为“堆叠的...

    栈的操作,堆和栈的区别

    堆空间的管理更加复杂,因为它涉及到内存碎片问题,以及垃圾回收机制在某些语言(如Java、C#)中的应用。 #### 堆和栈的主要区别 1. **生命周期和管理方式**: - **栈**:自动管理,随着函数调用的开始和结束而...

    一看就懂:图解C#中的值类型、引用类型、栈、堆、ref、out

    总的来说,理解C#中的值类型与引用类型、栈和堆的概念以及`ref`和`out`的使用,对于编写高效、无误的代码至关重要。它们涉及到内存管理、性能优化以及正确处理对象的生命周期。通过深入学习这些基础知识,开发者能够...

    [源代码]再探C#类与结构体究竟谁快

    "再探C#类与结构体究竟谁快——考虑栈变量、栈分配、64位整数、密封类.txt"很可能包含了详细的文章内容,分析了这些因素对类和结构体性能的影响。"TryPointerCall2005"和"TryPointerCall2010"可能是用于测试和比较类...

    武汉大学 C#数据结构与算法

    5. **课程内容**:可能包括基础数据结构的实现、算法设计与分析、复杂度理论、高级数据结构(如堆、B树等)、图算法、排序与查找算法的C#实现、动态规划和贪心策略等。 6. **学习资源**:“C#数据结构与算法_武汉...

    C# 语言规范 版本5.0中文.pdf

    版本控制是C#设计中的另一个重要考虑,它使得程序和库能够在更新后保持兼容性。例如,virtual和override关键字的独立使用,方法重载的规则,以及显式接口成员声明,都是为了支持版本控制。 C#的程序结构从简单的...

    c#中常见的问题

    ### C#中常见的问题知识点详解 #### 1. 什么是C#? C#(读作“C sharp”)是微软公司开发的一种面向对象的编程语言,它结合了C/C++和Java等多种语言的特点,旨在为程序员提供一种更现代、更高效的语言选择。C#在...

    C#中实现内存回收

    .NET中的内存分为堆和栈两部分。栈主要用于存储基本类型和引用,而堆则用于存储对象实例。当创建一个对象时,它的引用会被放在栈上,而对象本身则在堆上分配空间。垃圾回收主要关注堆上的内存。 垃圾回收的工作原理...

Global site tag (gtag.js) - Google Analytics