什么和堆栈和堆在哪里?

编程语言的书籍解释了值的types是在堆栈上创build的,参考types是在堆上创build的,而不解释这两件事情是什么。 我没有读到这个清楚的解释。 我明白什么是堆栈 ,但是他们在什么地方(真实的计算机内存)?

  • 它们在多大程度上由操作系统或语言运行时控制?
  • 他们的范围是什么?
  • 什么决定了他们每个的大小?
  • 是什么让一个更快?

堆栈是作为执行线程的暂存空间留出的内存。 当一个函数被调用时,一个块被保留在堆栈的顶部,用于局部variables和一些簿记数据。 当该函数返回时,该块将不被使用,并且可以在下一次调用函数时使用。 堆栈始终以LIFO(后进先出)顺序保留; 最近保留的块总是下一个要被释放的块。 这使得跟踪堆栈变得非常简单。 从堆栈释放一个块只不过是调整一个指针。

堆是为dynamic分配留出的内存。 与堆栈不同,没有强制的模式来分配和释放堆中的块; 你可以随时分配一个块,随时释放它。 这使得在任何给定时间跟踪堆的哪些部分被分配或释放变得更加复杂; 有许多自定义堆分配器可用于调整不同使用模式的堆性能。

每个线程都得到一个堆栈,而应用程序通常只有一个堆(尽pipe针对不同types的分配有多个堆并不罕见)。

直接回答你的问题:

它们在多大程度上由操作系统或语言运行时控制?

在创build线程时,OS为每个系统级线程分配堆栈。 通常,语言运行时会调用操作系统来为应用程序分配堆。

他们的范围是什么?

堆栈被附加到一个线程,所以当线程退出时,堆栈被回收。 堆通常在应用程序启动时由运行时分配,并在应用程序(技术上处理)退出时回收。

什么决定了他们每个的大小?

创build线程时设置堆栈的大小。 堆的大小是在应用程序启动时设置的,但可以随着空间需求而增长(分配器从操作系统请求更多的内存)。

是什么让一个更快?

堆栈更快,因为访问模式使分配和释放内存变得微不足道(指针/整数只是递增或递减),而堆在分配或解除分配中涉及更复杂的簿记。 另外,堆栈中的每个字节都会经常被重复使用,这意味着它往往会被映射到处理器的caching中,使其非常快速。 堆的另一个性能是,堆主要是全局资源,通常必须是multithreading安全的,即每个分配和释放都需要与程序中所有其他堆访问同步。

一个明确的示范:
图片来源: vikashazrati.wordpress.com

堆栈:

  • 像堆一样存储在计算机RAM中。
  • 在堆栈上创build的variables将超出范围,并自动释放。
  • 与堆上的variables相比,分配速度要快得多。
  • 用一个实际的堆栈数据结构来实现。
  • 存储本地数据,返回地址,用于parameter passing。
  • 如果使用了太多的堆栈(主要来自无限或太深的recursion,非常大的分配),可能会发生堆栈溢出。
  • 在堆栈上创build的数据可以在没有指针的情况下使用。
  • 如果您在编译之前确切知道需要分配多less数据,那么您将使用堆栈,并且它不是太大。
  • 程序启动时,通常已经确定了最大尺寸。

堆:

  • 像堆栈一样存储在计算机RAM中。
  • 在C ++中,堆上的variables必须手动销毁,永远不会超出范围。 数据被deletedelete[]free
  • 与堆栈中的variables相比,分配速度较慢。
  • 根据需要用来分配一块数据供程序使用。
  • 当有大量的分配和释放时,可以有碎片。
  • 在C ++或C中,在堆上创build的数据将由指针指向并分别用newmalloc分配。
  • 如果请求分配过大的缓冲区,可能会导致分配失败。
  • 如果您不知道在运行时需要多less数据,或者需要分配大量数据,则可以使用堆。
  • 负责内存泄漏。

例:

 int foo() { char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack). bool b = true; // Allocated on the stack. if(b) { //Create 500 bytes on the stack char buffer[500]; //Create 500 bytes on the heap pBuffer = new char[500]; }//<-- buffer is deallocated here, pBuffer is not }//<--- oops there's a memory leak, I should have called delete[] pBuffer; 

最重要的一点是堆和栈是可以分配内存的通用术语。 它们可以用许多不同的方式来实现,这些术语适用于基本概念。

  • 在一堆物品中,物品按照放置的顺序依次排列,而且只能删除最上面的物品(不会颠倒过来)。

    堆叠就像一堆文件

    一个堆栈的简单性在于你不需要维护一个包含分配内存的每个部分的logging的表; 您需要的唯一状态信息是指向堆栈结尾的单个指针。 要分配和取消分配,只需递增和递减该单个指针即可。 注意:一个堆栈有时可以实现从一段内存的顶部开始,向下延伸而不是向上延伸。

  • 在堆中,没有特定的顺序来放置物品。 您可以以任何顺序进入和取消物品,因为没有明确的“顶部”物品。

    堆像一堆甘草allsorts

    堆分配需要维护一个完整的内存分配和内存分配的logging,以及一些开销维护,以减less分段,find足够大的连续内存段以适应所需的大小,等等。 内存可以在任何时候释放空闲空间。 有时一个内存分配器将执行维护任务,比如通过移动分配的内存来对内存进行碎片整理,或者垃圾收集 – 在运行时识别内存不在范围内并释放内存。

这些图像在描述分配和释放堆栈和堆内存的两种方式时应该做得相当好。 百胜!

  • 它们在多大程度上由操作系统或语言运行时控制?

    如前所述,堆和栈是通用术语,并且可以以多种方式实现。 计算机程序通常有一个称为调用堆栈的堆栈 ,它存储与当前函数相关的信息,例如指向调用的函数的指针以及任何局部variables。 因为函数调用其他函数然后返回,所以堆栈增长和缩小以保存来自调用堆栈下方的函数的信息。 一个程序没有真正的运行时控制它; 这取决于编程语言,操作系统甚至系统架构。

    堆是一个通用术语,用于任何dynamic随机分配的内存; 即失序。 内存通常由OS分配,应用程序调用API函数来执行此分配。 pipe理dynamic分配的内存需要相当的开销,通常由操作系统来处理。

  • 他们的范围是什么?

    调用堆栈是一个低层次的概念,它与编程意义上的“范围”无关。 如果你反汇编一些代码,你会看到相对的指针风格引用到堆栈的某些部分,但就高级语言而言,这种语言强加了它自己的范围规则。 然而,堆栈的一个重要方面是,一旦一个函数返回,该函数的本地任何东西都立即从堆栈中被释放。 如果你的编程语言是如何工作的,那么你的工作方式就是这样。 在堆中,定义也很困难。 范围就是操作系统公开的任何东西,但是你的编程语言可能会增加关于你的应用程序中“范围”的规则。 处理器体系结构和操作系统使用虚拟寻址,处理器转换为物理地址,并存在页面错误等。它们跟踪哪些页面属于哪个应用程序。 但是,您从来不需要担心这一点,因为您只是使用编程语言使用的任何方法分配和释放内存,并检查错误(如果由于某种原因分配/释放失败)。

  • 什么决定了他们每个的大小?

    这又取决于语言,编译器,操作系统和体系结构。 堆栈通常是预先分配的,因为根据定义,堆栈必须是连续的内存(更多在最后一段)。 语言编译器或OS确定其大小。 你不会在堆栈中存储大量的数据,所以它将会大到永远不会被完全使用,除非在不必要的无限recursion的情况下(因此,“堆栈溢出”)或其他不寻常的编程决定。

    堆是可以dynamic分配的任何东西的总称。 根据你在哪个方面看,它不断变化的大小。 在现代的处理器和操作系统中,它的工作方式是非常抽象的,所以通常你不需要担心内存是如何工作的,除了(在它所允许的语言中)你不能使用内存你还没有分配或释放你已经释放的内存。

  • 是什么让一个更快?

    堆栈更快,因为所有空闲内存总是连续的。 没有列表需要维护空闲内存的所有段,只是指向当前栈顶的单个指针。 编译器通常将这个指针存储在一个特殊的快速寄存器中。 更重要的是,随后的堆栈操作通常集中在非常邻近的内存区域,这在很低的层次上对处理器内存caching的优化是有利的。

(我已经从另外一个或多或less是这个问题的问题提出了这个答案。)

您的问题的答案是具体实现,并可能在编译器和处理器体系结构中有所不同。 但是,这是一个简单的解释。

  • 堆栈和堆都是从底层操作系统分配的内存区域(通常是按需映射到物理内存的虚拟内存)。
  • 在multithreading环境中,每个线程都有自己完全独立的堆栈,但它们将共享堆。 并发访问必须在堆上进行控制,而不能在堆栈上进行。

  • 堆包含已使用和空闲块的链接列表。 堆中的新分配(通过newmalloc )通过从空闲块之一创build合适的块来满足。 这需要更新堆上的块列表。 关于堆中块的元信息也常常存储在每个块正前方的小区域中。
  • 随着堆的增长,新块通常从较低地址分配到较高地址。 因此,您可以将堆看作一堆随内存分配而增长的内存块。 如果分配的堆太小,通常可以通过从底层操作系统获取更多内存来增加堆的大小。
  • 分配和释放许多小块可能会使堆保持散布在所使用的块之间的许多小块空闲的状态。 分配大块的请求可能失败,因为即使空闲块的组合大小可能足够大,任何空闲块都不足以满足分配请求。 这被称为堆碎片
  • 当与空闲块相邻的使用块被解除分配时,新的空闲块可以与相邻的空闲块合并以创build更大的空闲块,从而有效地减less堆的碎片化。

堆

堆栈

  • 堆栈通常与CPU上一个名为堆栈指针的特殊寄存器串联在一起。 最初堆栈指针指向堆栈顶部(堆栈中的最高地址)。
  • CPU有特殊的指令入堆栈并从堆栈中popup 。 每次push都会将值存储在堆栈指针的当前位置,并减less堆栈指针。 一个pop检索堆栈指针指向的值,然后增加堆栈指针(不要被添加一个值到堆栈减less堆栈指针的事实所迷惑, 删除一个值会增加堆栈指针。底端)。 存储和检索的值是CPU寄存器的值。
  • 当一个函数被调用时,CPU使用推送当前指令指针的特殊指令,即在堆栈上执行的代码的地址。 CPU然后通过将指令指针设置为被调用函数的地址跳转到该函数。 之后,当函数返回时,旧的指令指针将从堆栈中popup,并在调用该函数之后的代码中继续执行。
  • 当input函数时,堆栈指针会减less,为本地(自动)variables在堆栈上分配更多的空间。 如果该函数具有一个本地32位variables,则将四个字节留在堆栈上。 当函数返回时,堆栈指针被移回以释放分配的区域。
  • 如果一个函数具有参数,那么在调用函数之前将这些参数压入堆栈。 然后函数中的代码就可以从当前堆栈指针向上浏览堆栈,以定位这些值。
  • 嵌套函数调用就像一个魅力。 每个新的调用将分配函数参数,局部variables的返回地址和空间,并且这些激活logging可以堆叠用于嵌套调用,并且当函数返回时将以正确的方式放松。
  • 由于堆栈是一个有限的内存块,通过调用过多的嵌套函数和/或为本地variables分配太多空间,可能导致堆栈溢出 。 通常用于堆栈的内存区域的设置方式使得在堆栈底部(最低地址)之下写入将触发CPU中的陷阱或exception。 这个exception情况可以被运行时捕获,并转换成某种堆栈溢出exception。

堆栈

一个函数可以分配堆而不是堆栈吗?

不是,函数的激活logging(即本地或自动variables)被分配在堆栈上,不仅用于存储这些variables,而且还用于跟踪嵌套的函数调用。

如何pipe理堆确实取决于运行时环境。 C使用malloc和C ++使用new ,但许多其他语言有垃圾收集。

但是,堆栈是与处理器架构紧密相关的更低级别的function。 当没有足够的空间时增长堆不是太难,因为它可以在处理堆的库调用中实现。 然而,堆栈的增长通常是不可能的,因为只有在发现堆栈溢出时才会发现堆栈溢出。 closures执行线程是唯一可行的select。

在下面的C#代码中

 public void Method1() { int i = 4; int y = 2; class1 cls1 = new class1(); } 

这是如何pipe理内存

堆栈上变量的图片

只要函数调用进入堆栈, Local Variables只需要持续。 堆被用于variables的生命周期,我们并不真正知道前面,但我们希望他们持续一段时间。 在大多数语言中,至关重要的一点是,如果我们想将它存储在堆栈上,我们知道在编译时有多大的variables。

对象(在我们更新它们时大小会有所不同)堆积如山,因为我们在创build时不知道它们要持续多久。 在许多语言中,堆是垃圾收集来查找不再有任何引用的对象(如cls1对象)。

在Java中,大多数对象直接进入堆中。 在像C / C ++这样的语言中,当你不处理指针时,结构和类通常可以保留在堆栈中。

更多信息可以在这里find:

堆栈和堆内存分配之间的区别«timmurphy.org

和这里:

在堆栈和堆上创build对象

本文是上图的来源: 六个重要的.NET概念:堆栈,堆,值types,引用types,装箱和拆箱 – CodeProject

但要注意它可能包含一些不准确的地方。

堆栈当你调用一个函数时,该函数的参数加上一些其他开销被放在堆栈上。 一些信息(例如返回的地方)也存储在那里。 当你在你的函数中声明一个variables时,这个variables也被分配到栈中。

取消分配堆栈非常简单,因为您总是按照分配的相反顺序释放。 在input函数时添加堆栈东西,相应的数据在您退出时被删除。 这意味着,除非调用大量调用其他函数的许多函数(或创buildrecursion解决scheme),否则您倾向于停留在堆栈的一小块区域内。

堆堆是一个通用名称,用于放置您即时创build的数据。 如果你不知道你的程序要创build多less个太空船,你很可能会使用新的(或malloc或相当的)操作符来创build每个太空船。 这个分配将会持续一段时间,所以很可能我们会按照与创build它们不同的顺序释放事物。

因此,堆是复杂得多,因为最后是没有使用的内存区域与内存碎片交错。 find你需要的大小的自由记忆是一个困难的问题。 这就是为什么堆应该避免(虽然它仍然经常使用)。

实现堆栈和堆的实现通常是运行时/操作系统。 通常游戏和其他对性能至关重要的应用程序都会创build自己的内存解决scheme,从堆中获取大量内存,然后在内部清除内存以避免依赖操作系统来存储内存。

这只有在你的内存使用情况与规范大不相同的情况下才适用 – 也就是说,对于在一个巨大的操作中加载一个级别的游戏,并且可以在另一个巨大的操作中将整个游戏卡住。

内存中的物理位置由于虚拟内存技术使您的程序认为您可以访问物理数据在其他地方(即使在硬盘上! 随着呼叫树越来越深,您获得堆栈的地址将不断增加。 堆的地址是不可预测的(即,特定于implimentation),坦率地说并不重要。

澄清, 这个答案有不正确的信息( 托马斯固定他的答案后,评论,酷:))。 其他答案只是避免解释什么静态分配手段。 所以我将解释三种主要的分配forms,以及它们通常如何与下面的堆,堆栈和数据段相关联。 我也会在C / C ++和Python中展示一些例子来帮助人们理解。

“静态”(AKA静态分配)variables不分配在堆栈上。 不要这样认为 – 很多人只是因为“静态”听起来很像“堆栈”。 它们实际上既不在堆栈中,也不在堆中。 这是所谓的数据段的一部分 。

但是,考虑“ 范围 ”和“ 生命期 ”而不是“堆栈”和“堆”通常会更好。

范围是指代码的哪些部分可以访问variables。 一般来说,我们认为局部范围 (只能由当前函数访问)与全局范围 (可以在任何地方访问),尽pipe范围可以变得复杂得多。

生命期是指在程序执行过程中分配和释放variables的时间。 通常我们会考虑静态分配 (variables将持续贯穿程序的整个持续时间,这对于在多个函数调用中存储相同的信息是有用的)与自动分配 (variables只在单个函数调用期间持续存在,对于存储只在你的函数中使用的信息,一旦完成就可以丢弃)和dynamic分配 (variables的持续时间是在运行时定义的,而不是象静态或自动的编译时间一样)。

尽pipe大多数编译器和解释器在使用堆栈,堆等方面类似地实现了这种行为,但只要行为是正确的,编译器有时也会打破这些约定。 例如,由于优化,局部variables可能只存在于寄存器中,或者被完全删除,即使大部分局部variables存在于堆栈中。 正如在一些评论中指出的那样,你可以自由地实现一个甚至不使用堆栈或堆的编译器,而是一些其他的存储机制(很less做,因为堆栈和堆是很好的)。

我将提供一些简单的注释C代码来说明所有这一切。 学习的最好方法是在debugging器下运行程序并观察行为。 如果你喜欢阅读python,跳到答案的结尾:)

 // Statically allocated in the data segment when the program/DLL is first loaded // Deallocated when the program/DLL exits // scope - can be accessed from anywhere in the code int someGlobalVariable; // Statically allocated in the data segment when the program is first loaded // Deallocated when the program/DLL exits // scope - can be accessed from anywhere in this particular code file static int someStaticVariable; // "someArgument" is allocated on the stack each time MyFunction is called // "someArgument" is deallocated when MyFunction returns // scope - can be accessed only within MyFunction() void MyFunction(int someArgument) { // Statically allocated in the data segment when the program is first loaded // Deallocated when the program/DLL exits // scope - can be accessed only within MyFunction() static int someLocalStaticVariable; // Allocated on the stack each time MyFunction is called // Deallocated when MyFunction returns // scope - can be accessed only within MyFunction() int someLocalVariable; // A *pointer* is allocated on the stack each time MyFunction is called // This pointer is deallocated when MyFunction returns // scope - the pointer can be accessed only within MyFunction() int* someDynamicVariable; // This line causes space for an integer to be allocated in the heap // when this line is executed. Note this is not at the beginning of // the call to MyFunction(), like the automatic variables // scope - only code within MyFunction() can access this space // *through this particular variable*. // However, if you pass the address somewhere else, that code // can access it too someDynamicVariable = new int; // This line deallocates the space for the integer in the heap. // If we did not write it, the memory would be "leaked". // Note a fundamental difference between the stack and heap // the heap must be managed. The stack is managed for us. delete someDynamicVariable; // In other cases, instead of deallocating this heap space you // might store the address somewhere more permanent to use later. // Some languages even take care of deallocation for you... but // always it needs to be taken care of at runtime by some mechanism. // When the function returns, someArgument, someLocalVariable // and the pointer someDynamicVariable are deallocated. // The space pointed to by someDynamicVariable was already // deallocated prior to returning. return; } // Note that someGlobalVariable, someStaticVariable and // someLocalStaticVariable continue to exist, and are not // deallocated until the program exits. 

A particularly poignant example of why it's important to distinguish between lifetime and scope is that a variable can have local scope but static lifetime – for instance, "someLocalStaticVariable" in the code sample above. Such variables can make our common but informal naming habits very confusing. For instance when we say " local " we usually mean " locally scoped automatically allocated variable " and when we say global we usually mean " globally scoped statically allocated variable ". Unfortunately when it comes to things like " file scoped statically allocated variables " many people just say… " huh??? ".

Some of the syntax choices in C/C++ exacerbate this problem – for instance many people think global variables are not "static" because of the syntax shown below.

 int var1; // Has global scope and static allocation static int var2; // Has file scope and static allocation int main() {return 0;} 

Note that putting the keyword "static" in the declaration above prevents var2 from having global scope. Nevertheless, the global var1 has static allocation. This is not intuitive! For this reason, I try to never use the word "static" when describing scope, and instead say something like "file" or "file limited" scope. However many people use the phrase "static" or "static scope" to describe a variable that can only be accessed from one code file. In the context of lifetime, "static" always means the variable is allocated at program start and deallocated when program exits.

Some people think of these concepts as C/C++ specific. 他们不是。 For instance, the Python sample below illustrates all three types of allocation (there are some subtle differences possible in interpreted languages that I won't get into here).

 from datetime import datetime class Animal: _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated def PetAnimal(self): curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood) class Cat(Animal): _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's class Dog(Animal): _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic! if __name__ == "__main__": whiskers = Cat() # Dynamically allocated fido = Dog() # Dynamically allocated rinTinTin = Dog() # Dynamically allocated whiskers.PetAnimal() fido.PetAnimal() rinTinTin.PetAnimal() Dog._FavoriteFood = 'milkbones' whiskers.PetAnimal() fido.PetAnimal() rinTinTin.PetAnimal() # Output is: # Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna # Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak # Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak # Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna # Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones # Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones 

Others have answered the broad strokes pretty well, so I'll throw in a few details.

  1. Stack and heap need not be singular. A common situation in which you have more than one stack is if you have more than one thread in a process. In this case each thread has its own stack. You can also have more than one heap, for example some DLL configurations can result in different DLLs allocating from different heaps, which is why it's generally a bad idea to release memory allocated by a different library.

  2. In C you can get the benefit of variable length allocation through the use of alloca , which allocates on the stack, as opposed to alloc, which allocates on the heap. This memory won't survive your return statement, but it's useful for a scratch buffer.

  3. Making a huge temporary buffer on Windows that you don't use much of is not free. This is because the compiler will generate a stack probe loop that is called every time your function is entered to make sure the stack exists (because Windows uses a single guard page at the end of your stack to detect when it needs to grow the stack. If you access memory more than one page off the end of the stack you will crash). 例:

 void myfunction() { char big[10000000]; // Do something that only uses for first 1K of big 99% of the time. } 

Others have directly answered your question, but when trying to understand the stack and the heap, I think it is helpful to consider the memory layout of a traditional UNIX process (without threads and mmap() -based allocators). The Memory Management Glossary web page has a diagram of this memory layout.

The stack and heap are traditionally located at opposite ends of the process's virtual address space. The stack grows automatically when accessed, up to a size set by the kernel (which can be adjusted with setrlimit(RLIMIT_STACK, ...) ). The heap grows when the memory allocator invokes the brk() or sbrk() system call, mapping more pages of physical memory into the process's virtual address space.

In systems without virtual memory, such as some embedded systems, the same basic layout often applies, except the stack and heap are fixed in size. However, in other embedded systems (such as those based on Microchip PIC microcontrollers), the program stack is a separate block of memory that is not addressable by data movement instructions, and can only be modified or read indirectly through program flow instructions (call, return, etc.). Other architectures, such as Intel Itanium processors, have multiple stacks . In this sense, the stack is an element of the CPU architecture.

I think many other people have given you mostly correct answers on this matter.

One detail that has been missed, however, is that the "heap" should in fact probably be called the "free store". The reason for this distinction is that the original free store was implemented with a data structure known as a "binomial heap." For that reason, allocating from early implementations of malloc()/free() was allocation from a heap. However, in this modern day, most free stores are implemented with very elaborate data structures that are not binomial heaps.

The stack is a portion of memory that can be manipulated via several key assembly language instructions, such as 'pop' (remove and return a value from the stack) and 'push' (push a value to the stack), but also call (call a subroutine – this pushes the address to return to the stack) and return (return from a subroutine – this pops the address off of the stack and jumps to it). It's the region of memory below the stack pointer register, which can be set as needed. The stack is also used for passing arguments to subroutines, and also for preserving the values in registers before calling subroutines.

The heap is a portion of memory that is given to an application by the operating system, typically through a syscall like malloc. On modern OSes this memory is a set of pages that only the calling process has access to.

The size of the stack is determined at runtime, and generally does not grow after the program launches. In a C program, the stack needs to be large enough to hold every variable declared within each function. The heap will grow dynamically as needed, but the OS is ultimately making the call (it will often grow the heap by more than the value requested by malloc, so that at least some future mallocs won't need to go back to the kernel to get more memory. This behavior is often customizable)

Because you've allocated the stack before launching the program, you never need to malloc before you can use the stack, so that's a slight advantage there. In practice, it's very hard to predict what will be fast and what will be slow in modern operating systems that have virtual memory subsystems, because how the pages are implemented and where they are stored is an implementation detail.

What is a stack?

A stack is a pile of objects, typically one that is neatly arranged.

在这里输入图像说明

Stacks in computing architectures are regions of memory where data is added or removed in a last-in-first-out manner.
In a multi-threaded application, each thread will have its own stack.

What is a heap?

A heap is an untidy collection of things piled up haphazardly.

在这里输入图像说明

In computing architectures the heap is an area of dynamically-allocated memory that is managed automatically by the operating system or the memory manager library.
Memory on the heap is allocated, deallocated, and resized regularly during program execution, and this can lead to a problem called fragmentation.
Fragmentation occurs when memory objects are allocated with small spaces in between that are too small to hold additional memory objects.
The net result is a percentage of the heap space that is not usable for further memory allocations.

Both together

In a multi-threaded application, each thread will have its own stack. But, all the different threads will share the heap.
Because the different threads share the heap in a multi-threaded application, this also means that there has to be some coordination between the threads so that they don't try to access and manipulate the same piece(s) of memory in the heap at the same time.

Which is faster – the stack or the heap? 为什么?

The stack is much faster than the heap.
This is because of the way that memory is allocated on the stack.
Allocating memory on the stack is as simple as moving the stack pointer up.

For people new to programming, it's probably a good idea to use the stack since it's easier.
Because the stack is small, you would want to use it when you know exactly how much memory you will need for your data, or if you know the size of your data is very small.
It's better to use the heap when you know that you will need a lot of memory for your data, or you just are not sure how much memory you will need (like with a dynamic array).

Java Memory Model

在这里输入图像说明

The stack is the area of memory where local variables (including method parameters) are stored. When it comes to object variables, these are merely references (pointers) to the actual objects on the heap.
Every time an object is instantiated, a chunk of heap memory is set aside to hold the data (state) of that object. Since objects can contain other objects, some of this data can in fact hold references to those nested objects.

You can do some interesting things with the stack. For instance, you have functions like alloca (assuming you can get past the copious warnings concerning its use), which is a form of malloc that specifically uses the stack, not the heap, for memory.

That said, stack-based memory errors are some of the worst I've experienced. If you use heap memory, and you overstep the bounds of your allocated block, you have a decent chance of triggering a segment fault. (Not 100%: your block may be incidentally contiguous with another that you have previously allocated.) But since variables created on the stack are always contiguous with each other, writing out of bounds can change the value of another variable. I have learned that whenever I feel that my program has stopped obeying the laws of logic, it is probably buffer overflow.

Simply, the stack is where local variables get created. Also, every time you call a subroutine the program counter (pointer to the next machine instruction) and any important registers, and sometimes the parameters get pushed on the stack. Then any local variables inside the subroutine are pushed onto the stack (and used from there). When the subroutine finishes, that stuff all gets popped back off the stack. The PC and register data gets and put back where it was as it is popped, so your program can go on its merry way.

The heap is the area of memory dynamic memory allocations are made out of (explicit "new" or "allocate" calls). It is a special data structure that can keep track of blocks of memory of varying sizes and their allocation status.

In "classic" systems RAM was laid out such that the stack pointer started out at the bottom of memory, the heap pointer started out at the top, and they grew towards each other. If they overlap, you are out of RAM. That doesn't work with modern multi-threaded OSes though. Every thread has to have its own stack, and those can get created dynamicly.

From WikiAnwser.

When a function or a method calls another function which in turns calls another function, etc., the execution of all those functions remains suspended until the very last function returns its value.

This chain of suspended function calls is the stack, because elements in the stack (function calls) depend on each other.

The stack is important to consider in exception handling and thread executions.

The heap is simply the memory used by programs to store variables. Element of the heap (variables) have no dependencies with each other and can always be accessed randomly at any time.

  • Very fast access
  • Don't have to explicitly de-allocate variables
  • Space is managed efficiently by CPU, memory will not become fragmented
  • Local variables only
  • Limit on stack size (OS-dependent)
  • Variables cannot be resized

Heap

  • Variables can be accessed globally
  • No limit on memory size
  • (Relatively) slower access
  • No guaranteed efficient use of space, memory may become fragmented over time as blocks of memory are allocated, then freed
  • You must manage memory (you're in charge of allocating and freeing variables)
  • Variables can be resized using realloc()

In the 1980s, UNIX propagated like bunnies with big companies rolling their own. Exxon had one as did dozens of brand names lost to history. How memory was laid out was at the discretion of the many implementors.

A typical C program was laid out flat in memory with an opportunity to increase by changing the brk() value. Typically, the HEAP was just below this brk value and increasing brk increased the amount of available heap.

The single STACK was typically an area below HEAP which was a tract of memory containing nothing of value until the top of the next fixed block of memory. This next block was often CODE which could be overwritten by stack data in one of the famous hacks of its era.

One typical memory block was BSS (a block of zero values) which was accidentally not zeroed in one manufacturer's offering. Another was DATA containing initialized values, including strings and numbers. A third was CODE containing CRT (C runtime), main, functions, and libraries.

The advent of virtual memory in UNIX changes many of the constraints. There is no objective reason why these blocks need be contiguous, or fixed in size, or ordered a particular way now. Of course, before UNIX was Multics which didn't suffer from these constraints. Here is a schematic showing one of the memory layouts of that era.

A typical 1980s style UNIX C program memory layout

  • 介绍

Physical memory is the range of the physical addresses of the memory cells in which an application or system stores its data, code, and so on during execution. Memory management denotes the managing of these physical addresses by swapping the data from physical memory to a storage device and then back to physical memory when needed. The OS implements the memory management services using virtual memory. As a C# application developer you do not need to write any memory management services. The CLR uses the underlying OS memory management services to provide the memory model for C# or any other high-level language targeting the CLR.

Figure 4-1 shows physical memory that has been abstracted and managed by the OS, using the virtual memory concept. Virtual memory is the abstract view of the physical memory, managed by the OS. Virtual memory is simply a series of virtual addresses, and these virtual addresses are translated by the CPU into the physical address when needed.

Figure 4-1. CLR memory abstraction

在这里输入图像说明

The CLR provides the memory management abstract layer for the virtual execution environment, using the operating memory services. The abstracted concepts the CLR uses are AppDomain, thread, stack, heapmemorymapped file, and so on. The concept of the application domain (AppDomain) gives your application an isolated execution environment.

  • Memory Interaction between the CLR and OS

By looking at the stack trace while debugging the following C# application, using WinDbg, you will see how the CLR uses the underlying OS memory management services (eg, the HeapFree method from KERNEL32.dll, the RtlpFreeHeap method from ntdll.dll) to implement its own memory model:

 using System; namespace CH_04 { class Program { static void Main(string[] args) { Book book = new Book(); Console.ReadLine(); } } public class Book { public void Print() { Console.WriteLine(ToString()); } } } 

The compiled assembly of the program is loaded into WinDbg to start debugging. You use the following commands to initialize the debugging session:

0:000> sxe ld clrjit

0:000> g

0:000> .loadby sos clr

0:000> .load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll

Then, you set a breakpoint at the Main method of the Program class, using the !bpmd command:

0:000>!bpmd CH_04.exe CH_04.Program.Main

To continue the execution and break at the breakpoint, execute the g command:

0:000> g

When the execution breaks at the breakpoint, you use the !eestack command to view the stack trace details of all threads running for the current process. The following output shows the stack trace for all the threads running for the application CH_04.exe:

0:000> !eestack


Thread 0

Current frame: (MethodDesc 00233800 +0 CH_04.Program.Main(System.String[]))

ChildEBP RetAddr Caller, Callee

0022ed24 5faf21db clr!CallDescrWorker+0x33

/ trace removed /

0022f218 77712d68 ntdll!RtlFreeHeap+0x142, calling ntdll!RtlpFreeHeap

0022f238 771df1ac KERNEL32!HeapFree+0x14, calling ntdll!RtlFreeHeap

0022f24c 5fb4c036 clr!EEHeapFree+0x36, calling KERNEL32!HeapFree

0022f260 5fb4c09d clr!EEHeapFreeInProcessHeap+0x24, calling clr!EEHeapFree

0022f274 5fb4c06d clr!operator delete[]+0x30, calling clr!EEHeapFreeInProcessHeap / trace removed /

0022f4d0 7771316f ntdll!RtlpFreeHeap+0xb7a, calling ntdll!_SEH_epilog4

0022f4d4 77712d68 ntdll!RtlFreeHeap+0x142, calling ntdll!RtlpFreeHeap

0022f4f4 771df1ac KERNEL32!HeapFree+0x14, calling ntdll!RtlFreeHeap

/ trace removed /

This stack trace indicates that the CLR uses OS memory management services to implement its own memory model. Any memory operation in.NET goes via the CLR memory layer to the OS memory management layer.

Figure 4-2 illustrates a typical C# application memory model used by the CLR at runtime.

Figure 4-2 . A typical C# application memory model 在这里输入图像描述

The CLR memory model is tightly coupled with the OS memory management services. To understand the CLR memory model, it is important to understand the underlying OS memory model. It is also crucial to know how the physical memory address space is abstracted into the virtual memory address space, the ways the virtual address space is being used by the user application and system application, how virtual-to-physical address mapping works, how memory-mapped file works, and so on. This background knowledge will improve your grasp of CLR memory model concepts, including AppDomain, stack, and heap.

For more information, refer to this book:

C# Deconstructed: Discover how C# works on the .NET Framework

This book + ClrViaC# + Windows Internals are excellent resources to known .net framework in depth and relation with OS.

In Sort

A stack is used for static memory allocation and a heap for dynamic memory allocation, both stored in the computer's RAM.


In Detail

The Stack

The stack is a "LIFO" (last in, first out) data structure, that is managed and optimized by the CPU quite closely. Every time a function declares a new variable, it is "pushed" onto the stack. Then every time a function exits, all of the variables pushed onto the stack by that function, are freed (that is to say, they are deleted). Once a stack variable is freed, that region of memory becomes available for other stack variables.

The advantage of using the stack to store variables, is that memory is managed for you. You don't have to allocate memory by hand, or free it once you don't need it any more. What's more, because the CPU organizes stack memory so efficiently, reading from and writing to stack variables is very fast.

More can be found here .


The Heap

The heap is a region of your computer's memory that is not managed automatically for you, and is not as tightly managed by the CPU. It is a more free-floating region of memory (and is larger). To allocate memory on the heap, you must use malloc() or calloc(), which are built-in C functions. Once you have allocated memory on the heap, you are responsible for using free() to deallocate that memory once you don't need it any more.

If you fail to do this, your program will have what is known as a memory leak. That is, memory on the heap will still be set aside (and won't be available to other processes). As we will see in the debugging section, there is a tool called Valgrind that can help you detect memory leaks.

Unlike the stack, the heap does not have size restrictions on variable size (apart from the obvious physical limitations of your computer). Heap memory is slightly slower to be read from and written to, because one has to use pointers to access memory on the heap. We will talk about pointers shortly.

Unlike the stack, variables created on the heap are accessible by any function, anywhere in your program. Heap variables are essentially global in scope.

More can be found here .


Variables allocated on the stack are stored directly to the memory and access to this memory is very fast, and its allocation is dealt with when the program is compiled. When a function or a method calls another function which in turns calls another function, etc., the execution of all those functions remains suspended until the very last function returns its value. The stack is always reserved in a LIFO order, the most recently reserved block is always the next block to be freed. This makes it really simple to keep track of the stack, freeing a block from the stack is nothing more than adjusting one pointer.

Variables allocated on the heap have their memory allocated at run time and accessing this memory is a bit slower, but the heap size is only limited by the size of virtual memory. Elements of the heap have no dependencies with each other and can always be accessed randomly at any time. You can allocate a block at any time and free it at any time. This makes it much more complex to keep track of which parts of the heap are allocated or free at any given time.

在这里输入图像说明

You can use the stack if you know exactly how much data you need to allocate before compile time, and it is not too big. You can use the heap if you don't know exactly how much data you will need at runtime or if you need to allocate a lot of data.

In a multi-threaded situation each thread will have its own completely independent stack, but they will share the heap. The stack is thread specific and the heap is application specific. The stack is important to consider in exception handling and thread executions.

Each thread gets a stack, while there's typically only one heap for the application (although it isn't uncommon to have multiple heaps for different types of allocation).

在这里输入图像说明

At run-time, if the application needs more heap, it can allocate memory from free memory and if the stack needs memory, it can allocate memory from free memory allocated memory for the application.

Even, more detail is given here and here .


Now come to your question's answers .

To what extent are they controlled by the OS or language runtime?

The OS allocates the stack for each system-level thread when the thread is created. Typically the OS is called by the language runtime to allocate the heap for the application.

More can be found here .

What is their scope?

Already given in top.

"You can use the stack if you know exactly how much data you need to allocate before compile time, and it is not too big. You can use the heap if you don't know exactly how much data you will need at runtime or if you need to allocate a lot of data."

More can be found in here .

What determines the size of each of them?

The size of the stack is set by OS when a thread is created. The size of the heap is set on application startup, but it can grow as space is needed (the allocator requests more memory from the operating system).

What makes one faster?

堆栈分配速度要快得多,因为所有的操作都是移动堆栈指针。 使用内存池,您可以从堆分配中获得可比较的性能,但是稍微增加了一些复杂性和自己的麻烦。

另外,堆栈与堆不仅仅是性能的考虑因素; 它也会告诉你很多事物的预期寿命。

Details can be found from here .

Since some answers went nitpicking, I'm going to contribute my mite.

Surprisingly, no one has mentioned that multiple (ie not related to the number of running OS-level threads) call stacks are to be found not only in exotic languages (PostScript) or platforms (Intel Itanium), but also in fibers , green threads and some implementations of coroutines .

Fibers, green threads and coroutines are in many ways similar, which leads to much confusion. The difference between fibers and green threads is that the former use cooperative multitasking, while the latter may feature either cooperative or preemptive one (or even both). For the distinction between fibers and coroutines, see here .

In any case, the purpose of both fibers, green threads and coroutines is having multiple functions executing concurrently, but not in parallel (see this SO question for the distinction) within a single OS-level thread, transferring control back and forth from one another in an organized fashion.

When using fibers, green threads or coroutines, you usually have a separate stack per function. (Technically, not just a stack but a whole context of execution is per function. Most importantly, CPU registers.) For every thread there're as many stacks as there're concurrently running functions, and the thread is switching between executing each function according to the logic of your program. When a function runs to its end, its stack is destroyed. So, the number and lifetimes of stacks are dynamic and are not determined by the number of OS-level threads!

Note that I said " usually have a separate stack per function". There're both stackful and stackless implementations of couroutines. Most notable stackful C++ implementations are Boost.Coroutine and Microsoft PPL 's async/await . (However, C++'s resumable functions (aka " async and await "), which were proposed to C++17, are likely to use stackless coroutines.)

Fibers proposal to the C++ standard library is forthcoming. Also, there're some third-party libraries . Green threads are extremely popular in languages like Python and Ruby.

A couple of cents: I think, it will be good to draw memory graphical and more simple:

This is my vision of process memory construction with simplification for more easy understanding wht happening

Arrows – show where grow stack and heap, process stack size have limit, defined in OS, thread stack size limits by parameters in thread create API usually. Heap usually limiting by process maximum virtual memory size, for 32 bit 2-4 GB for example.

So simple way: process heap is general for process and all threads inside, using for memory allocation in common case with something like malloc() .

Stack is quick memory for store in common case function return pointers and variables, processed as parameters in function call, local function variables.

stack , heap and data of each process in virtual memory:

stack, heap and data of each process in virtual memory

OK, simply and in short words, they mean ordered and not ordered !

Stack : In stack items, things get on the top of each-other, means gonna be faster and more efficient to be processed!…

So there is always an index to point the specific item, also processing gonna be faster, there is relationship between the items as well!…

Heap : No order, processing gonna be slower and values are messed up together with no specific order or index… there are random and there is no relationship between them… so execution and usage time could be vary…

I also create the image below to show how they may look like:

在这里输入图像描述

A lot of answers are correct as concepts, but we must note that a stack is needed by the hardware (ie microprocessor) to allow calling subroutines (CALL in assembly language..). (OOP guys will call it methods )

On the stack you save return addresses and call → push / ret → pop is managed directly in hardware.

You can use the stack to pass parameters.. even if it is slower than using registers (would a microprocessor guru say or a good 1980s BIOS book…)

  • Without stack no microprocessor can work. (we can't imagine a program, even in assembly language, without subroutines/functions)
  • Without the heap it can. (An assembly language program can work without, as the heap is a OS concept, as malloc, that is a OS/Lib call.

Stack usage is faster as:

  • Is hardware, and even push/pop are very efficient.
  • malloc requires entering kernel mode, use lock/semaphore (or other synchronization primitives) executing some code and manage some structures needed to keep track of allocation.