编程语言书籍解释了在堆栈上创建了值类型,并且在堆上创建了引用类型,而没有解释这两者是什么。我还没有看到对此的明确解释。我理解堆栈是什么。但,
堆栈是作为执行线程的临时空间留出的内存。调用函数时,在堆栈顶部保留一个块,用于本地变量和一些簿记数据。当该函数返回时,该块将变为未使用状态,并可在下次调用函数时使用。堆栈始终以 LIFO(后进先出)顺序保留; 最近保留的块始终是要释放的下一个块。这使得跟踪堆栈非常简单; 从堆栈中释放块只不过是调整一个指针。
堆是为动态分配留出的内存。与堆栈不同,堆中的块的分配和释放没有强制模式; 您可以随时分配一个块并随时释放它。这使得在任何给定时间跟踪堆的哪些部分被分配或释放变得更加复杂; 有许多自定义堆分配器可用于调整不同使用模式的堆性能。
每个线程都有一个堆栈,而应用程序通常只有一个堆(尽管为不同类型的分配设置多个堆并不罕见)。
直接回答您的问题:
它们在多大程度上受操作系统或语言运行时控制?
操作系统在创建线程时为每个系统级线程分配堆栈。通常,语言运行库调用 OS 来为应用程序分配堆。
它们的范围是什么?
堆栈附加到一个线程,因此当线程退出堆栈时将被回收。堆通常在应用程序启动时由运行时分配,并在应用程序(技术过程)退出时回收。
是什么决定了它们的大小?
创建线程时设置堆栈的大小。堆的大小在应用程序启动时设置,但可以在需要空间时增长(分配器从操作系统请求更多内存)。
是什么让一个更快?
堆栈更快,因为访问模式使得从中分配和释放内存变得微不足道(指针 / 整数简单地递增或递减),而堆在分配或释放中涉及更复杂的簿记。此外,堆栈中的每个字节都经常被频繁地重用,这意味着它往往被映射到处理器的缓存,使其非常快。堆的另一个性能影响是堆(主要是全局资源)通常必须是多线程安全的,即每个分配和释放需要 - 通常 - 与程序中的 “所有” 其他堆访问同步。
明确的示范:
图片来源: vikashazrati.wordpress.com
堆栈:
堆:
delete
, delete[]
或free
释放数据。 new
或malloc
分配。 例:
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;
最重要的一点是堆和堆栈是可以分配内存的通用术语。它们可以以多种不同的方式实现,并且这些术语适用于基本概念。
在一堆物品中,物品按照它们放置在那里的顺序一个在另一个上面,你只能移除顶部的物品(不会翻倒整个物品)。
堆栈的简单性在于您不需要维护包含已分配内存的每个部分的记录的表; 您需要的唯一状态信息是指向堆栈末尾的单个指针。要分配和取消分配,只需递增和递减该单个指针即可。注意:有时可以实现堆栈从一部分内存的顶部开始并向下延伸而不是向上扩展。
在堆中,对项目的放置方式没有特定的顺序。您可以按任意顺序进入和移除商品,因为没有明确的 “顶部” 商品。
堆分配需要维护已分配内存和不分配内存的完整记录,以及减少碎片的一些开销维护,找到足以满足请求大小的连续内存段,等等。内存可以在任何时候释放,留下空闲空间。有时,内存分配器将执行维护任务,例如通过移动已分配的内存来对内存进行碎片整理,或者进行垃圾收集 - 在内存不再占用范围并在解除分配时在运行时进行识别。
这些图像应该可以很好地描述在堆栈和堆中分配和释放内存的两种方法。百胜!
它们在多大程度上受操作系统或语言运行时控制?
如上所述,堆和堆栈是通用术语,可以通过多种方式实现。计算机程序通常具有称为调用堆栈的堆栈 ,其存储与当前函数相关的信息,例如指向调用它的任何函数的指针,以及任何局部变量。因为函数调用其他函数然后返回,所以堆栈增大和缩小以保持来自调用堆栈中的函数的信息。程序实际上没有对它进行运行时控制; 它取决于编程语言,操作系统甚至系统架构。
堆是一个通用术语,用于动态和随机分配的任何内存; 即无序。内存通常由 OS 分配,应用程序调用 API 函数来执行此分配。管理动态分配的内存需要相当大的开销,这通常由操作系统处理。
它们的范围是什么?
调用堆栈是一种低级概念,它与编程意义上的 “范围” 无关。如果您反汇编某些代码,您将看到对堆栈部分的相对指针样式引用,但就更高级别的语言而言,该语言强加了自己的范围规则。但是,堆栈的一个重要方面是,一旦函数返回,该函数的任何本地函数都会立即从堆栈中释放出来。考虑到编程语言的工作原理,它的工作方式与预期的方式相同。在堆中,它也很难定义。范围是操作系统公开的内容,但是您的编程语言可能会添加有关应用程序中 “范围” 的规则。处理器体系结构和 OS 使用虚拟寻址,处理器将虚拟寻址转换为物理地址,并存在页面错误等。它们跟踪哪些页面属于哪些应用程序。但是,您永远不必担心这一点,因为您只需使用编程语言用于分配和释放内存的任何方法,并检查错误(如果分配 / 释放因任何原因失败)。
是什么决定了它们的大小?
同样,它取决于语言,编译器,操作系统和体系结构。堆栈通常是预先分配的,因为根据定义它必须是连续的内存(更多内容在最后一段中)。语言编译器或 OS 确定其大小。您不会在堆栈上存储大量数据,因此除非出现不必要的无限递归(因此,“堆栈溢出”)或其他异常编程决策,否则它应该永远不会被完全使用。
堆是可以动态分配的任何东西的通用术语。根据您看待它的方式,它会不断变化大小。在现代处理器和操作系统中,它的工作方式无论如何都是非常抽象的,所以你通常不需要担心它如何深入工作,除了(在它允许你的语言中)你不能使用的内存你尚未分配或已释放的记忆。
是什么让一个更快?
堆栈更快,因为所有可用内存始终是连续的。不需要维护所有可用内存段的列表,只需要指向当前堆栈顶部的单个指针。为此,编译器通常将此指针存储在一个特殊的快速寄存器中。更重要的是,堆栈上的后续操作通常集中在非常靠近的内存区域,这非常低的水平有利于处理器片上高速缓存的优化。