.Net栈和堆详解

什么是栈堆

  在计算机领域,堆栈是一个不容忽视的概念,栈堆是两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。要点:堆,队列优先,先进先出(FIFO—first in first out);栈,先进后出(FILO—First-In/Last-Out)。

  堆栈是一个在计算机科学中经常使用的抽象数据类型。堆栈中的物体具有一个特性: 最后一个放入堆栈中的物体总是被最先拿出来, 这个特性通常称为后进先出(LIFO)队列。

栈和堆的区别

堆栈空间分配

  栈(操作系统):由操作系统自动分配释放,存放函数的变量值,局部变量的值等等,其操作方式类似于数据结构中的栈;

  堆(操作系统):一般由开发者分配释放,若不释放,程序结束时可能会有OS回收,分配方式倒是类似于链表;

堆栈的缓存方式

  栈使用的是一级缓存,通常是被调用时处于储存空间,调用完后自动释放;

  堆使用的是二级缓存,生命周期有虚拟机的垃圾回收算法来决定(并不一定成为孤儿对象就被立即释放)。所以调用这些对象的速度相对来得慢一些;

堆栈数据结构的区别

  堆(数据结构):堆可以看做是一棵树;例如:堆排序;

  栈(数据结构):一种先进后出的数据结构;

区别介绍

  栈负责保存我们的代码执行(或调用)路,而堆则负责保存对象(或者说数据)的路径。

  可以将栈想象成一堆从顶向下堆叠的盒子。当每调用一次方法时,我们将应用程序中所要发生的事情记录在栈顶的一个盒子中,而我们每次只能够使用栈顶的那个盒子。当我们栈顶的盒子被使用完之后,或者说方法执行完毕之后,我们将抛开这个盒子然后继续使用栈顶上的新盒子。堆的工作原理比较相似,但大多数时候堆用作保存信息而非保存执行路径,因此堆能够在任意时间被访问。与栈相比堆没有任何访问限制,堆就像床上的旧衣服,我们并没有花时间去整理,那是因为可以随时找到一件我们需要的衣服,而栈就像储物柜里堆叠的鞋盒,我们只能从最顶层的盒子开始取,直到发现那只合适的。

  

  以上图片并不是内存中真实的表现形式,但能够帮助我们区分栈和堆。

  栈是自行维护的,也就是说内存自动维护栈,当栈顶的盒子不再被使用,它将被抛出。相反的,堆需要考虑垃圾回收,垃圾回收用于保持堆的整洁性,没有人愿意看到周围都是赃衣服,那简直太臭了!

  当我们的代码执行的时候,栈和堆中主要放置了四种类型的数据:值类型(Value Type),引用类型(Reference Type),指针(Pointer),指令(Instruction)。

  1.值类型

  bool,byte ,char,decimal,double,enum,float,int,long,sbyte,short,struct,uint,ulong,ushort

  2.引用类型

  class,interface,delegate ,object ,string

  3.指针

  在内存管理方案中放置的第三种类型是类型引用,引用通常就是一个指针。我们不会显示的使用指针,它们由公共语言运行时(CLR)来管理。指针(或引用)是不同于引用类型的,是因为当我们说某个事物是一个引用类型时就意味着我们是通过指针来访问它的。指针是一块内存空间,而它指向另一个内存空间。就像栈和堆一样,指针也同样要占用内存空间,但它的值是一个内存地址或者为空。

  

  如上图,可以帮我们更加好理解堆和栈,在装箱和拆箱中也能体现

  堆栈使用情况

  1. 引用类型总是放在堆中。

  2.值类型和指针总是放在它们被声明的地方。

  就像我们先前提到的,栈是负责保存我们的代码执行(或调用)时的路径。当我们的代码开始调用一个方法时,将放置一段编码指令(在方法中)到栈上,紧接着放置方法的参数,然后代码执行到方法中的被“压栈”至栈顶的变量位置。通过以下例子很容易理解...

  定义一个方法  

public int AddFive(int pValue)
    {
                int result;
                result = pValue + 5;
                return result;
    }

  现在就来看看在栈顶发生了些什么,记住我们所观察的栈顶下实际已经压入了许多别的内容。

  首先方法(只包含需要执行的逻辑字节,即执行该方法的指令,而非方法体内的数据)入栈,紧接着是方法的参数入栈。

  

  接着,控制(即执行方法的线程)被传递到堆栈中AddFive()的指令上

  

  当方法执行时,我们需要在栈上为“result”变量分配一些内存

  

  方法执行完成,然后方法的结果被返回。

  

  通过将栈指针指向AddFive()方法曾使用的可用的内存地址,所有在栈上的该方法所使用内存都被清空,且程序将自动回到栈上最初的方法调用的位置。

  

  

  在这个例子中,我们的"result"变量是被放置在栈上的,事实上,当值类型数据在方法体中被声明时,它们都是被放置在栈上的。

  值类型数据有时也被放置在堆上。记住这条规则--值类型总是放在它们被声明的地方。好的,如果一个值类型数据在方法体外被声明,且存在于一个引用类型中,那么它将被堆中的引用类型所取代。

  来看另一个例子:  

        //假如我们有这样一个MyInt类(它是引用类型因为它是一个类类型):

          public class MyInt
          {
             public int MyValue;
          }

        //然后执行下面的方法:

          public MyInt AddFive(int pValue)
          {
                MyInt result = new MyInt();
                result.MyValue = pValue + 5;
                return result;
          } 

  就像前面提到的,方法及方法的参数被放置到栈上,接下来,控制被传递到堆栈中AddFive()的指令上。

    

  接着会出现一些有趣的现象...

  因为"MyInt"是一个引用类型,它将被放置在堆上,同时在栈上生成一个指向这个堆的指针引用。

  

  在AddFive()方法被执行之后,我们将清空...

  

  我们将剩下孤独的MyInt对象在堆中(栈中将不会存在任何指向MyInt对象的指针!)

  

  这就是垃圾回收器(后简称GC)起作用的地方。当我们的程序达到了一个特定的内存阀值,我们需要更多的堆空间的时候,GC开始起作用。GC将停止所有正在运行的线程,找出在堆中存在的所有不再被主程序访问的对象,并删除它们。然后GC会重新组织堆中所有剩下的对象来节省空间,并调整栈和堆中所有与这些对象相关的指针。你肯定会想到这个过程非常耗费性能,所以这时你就会知道为什么我们需要如此重视栈和堆里有些什么,特别是在需要编写高性能的代码时。

  当我们使用引用类型时,我们实际是在处理该类型的指针,而非该类型本身。当我们使用值类型时,我们是在使用值类型本身。

  案例:

  执行以下方法  

    public int ReturnValue()
          {
                int x = new int();
                x = 3;
                int y = new int();
                y = x;
                y = 4;
                return x;
          }

  最后结果为3

  假如我们首先使用MyInt类  

        public class MyInt
          {
                public int MyValue;
          }
        //接着执行以下的方法:
          public int ReturnValue2()
          {
                MyInt x = new MyInt();
                x.MyValue = 3;
                MyInt y = new MyInt();
                y = x;
                y.MyValue = 4; //此时操作的是堆
                return x.MyValue;
          }    

  最后结果为4

  为什么?...  x.MyValue怎么会变成4了呢?...  看看我们所做的然后就知道是怎么回事了:

  在第一例子中,一切都像计划的那样进行着:

  

  在第二个例子中,我们没有得到"3"是因为变量"x"和"y"都同时指向了堆中相同的对象。

  

  

时间: 2024-08-02 15:13:34

.Net栈和堆详解的相关文章

C/C++堆、栈及静态数据区详解

转自:https://www.cnblogs.com/hanyonglu/archive/2011/04/12/2014212.html  做略微修改 本文介绍C/C++中堆,栈及静态数据区. 五大内存分区 在C++中,内存分成5个区,他们分别是堆.栈.自由存储区.全局/静态存储区和常量存储区.下面分别来介绍: 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区.里面的变量通常是局部变量.函数参数等. 堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用

ELK技术栈之-Logstash详解

ELK技术栈之-Logstash详解 前言 在第九章节中,我们已经安装好Logstash组件了,并且启动实例测试它的数据输入和输出,但是用的是最简单的控制台标准输入和标准输出,那这节我们就来深入的学习Logstash的详细使用. 常用启动参数 我们在上一节中演示了启动Logstash的实例,其中我们启动的时候给Logstash脚本传入了-e的参数,但实际上,Logstash的启动参数有很多,我们来看一下各个启动参数的作用: -e #立即启动实例,例如:./logstash -e "input {

JVM堆 栈 方法区详解

一.栈 每当启用一个线程时,JVM就为他分配一个JAVA栈,栈是以帧为单位保存当前线程的运行状态 栈是由栈帧组成,每当线程调用一个java方法时,JVM就会在该线程对应的栈中压入一个帧 只有在调用一个方法时,才为当前栈分配一个帧,然后将该帧压入栈 栈帧帧是由局部变量区.操作数栈和帧数据区组成 java栈上的所有数据都是私有的,任何线程都不能访问另一个线程的栈数据 局部变量区  调用方法时,类型信息确定此方法局部变量区和操作数栈的大小 局部变量区被组织为以一个字长为单位.从0开始计数的数组,类型为

堆和栈的区别详解

前言: 在程序设计的时候.堆栈总是不可避免的会接触到.而对于堆和栈他们的区别.在程序运行时各自的作用,如何利用堆栈提高运行效率等 很多人都还了解的不够,今天google了很多文章,所以在这里作个完善总结,希望能给有心人些帮助.有不足的地方还希望能指出. 栈是随函数被调用时分配的空间 栈上分配的空间是临时的,在函数退出后将被系统释放,不会造成内存泄露,不得用delete或free操作,因为栈的空间小所以在栈上不能获得大量的内存块 ,一般最大也就不到10M 堆是在整个进程的未分配空间中分配的内存,由

C/C++---printf/cout 从右至左压栈顺序实例详解

__cdecl压栈顺序实例 明白计算:计算是从右到左计算的 栈和寄存器变量:x++,是将计算结果存放到栈空间,最后是要出栈的:而++x和x是将计算结果直接存放到某个寄存器变量中(是同一个),所以计算完最后输出时,++x和x的结果总是相同的. ??用个小例子来说明下: int x=5; printf("%d %d\n",x,x++); int y=5; printf("%d %d\n",y++,y); int z=5; printf("%d %d %d\n&

函数调用过程栈帧变化详解

http://www.cnblogs.com/fxplove/articles/2574451.html 数调用另一个词语表示叫作 过程.一个过程调用包括将数据和控制从代码的一部分传递到另一部分.另外,它还必须在进入时为过程的局部变量分配空间,并在推出时释放这些空间.而数据传递,局部变量的分配和释放通过操纵程序栈来实现.在了解本文章之前,您需要先对程序的进程空间有所了解,即对进程如何使用内存?如果你知道这些,下面的内容将是很easy的事情了.为了您的回顾还是将简单的分布图贴出来,便于您的回顾.

Java堆/栈/常量池以及String的详细详解(转)------经典易懂系统

一:在JAVA中,有六个不同的地方可以存储数据: 1. 寄存器(register). 这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部.但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配.你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象. ------最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 堆栈(stack).位于通用RAM中,但通过它的“堆栈指针”可以从处理器哪里获得支持.堆栈指针若向下移动,则分配新的内存:若向上移动,则释放那

Java中堆内存和栈内存详解2

Java中堆内存和栈内存详解 Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用. 堆内存用于存放由new创建的对象和数组.在堆中分配的内存,由java虚拟机自动垃圾回收器来管理.在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的

(转载)C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区

C++内存分配方式详解--堆.栈.自由存储区.全局/静态存储区和常量存储区       栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区.里面的变量通常是局部变量.函数参数等.在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用.和堆一样,用户栈在程序执行期间可以动态地扩展和收缩. 堆,就是那些由 new 分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个 new 就要对应一个 delete.如果程序员没有释放掉,那么在程序结