什么和堆栈和堆在哪里?
编程语言书籍解释了值类型是在堆栈上创建的,参考类型是在堆上创建的,而没有解释这两件事是什么。 我没有读到这个清楚的解释。 我明白什么是堆栈,但他们在什么地方(什么是物理地位于真实计算机的内存中)?
堆栈是作为执行线程的暂存空间留出的内存。 当一个函数被调用时,一个块被保留在堆栈的顶部,用于局部变量和一些簿记数据。 当该函数返回时,该块将变为未使用,并且可以在下次调用某个函数时使用该块。 堆栈始终以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确定其大小。 您不会在堆栈中存储大量数据,因此它将会大到永远不会被完全使用,除非在不必要的无限递归(因此,“堆栈溢出”)或其他不寻常的编程决定的情况下。
堆是可以动态分配的任何东西的总称。 根据你看它的方式,它不断变化的大小。 在现代处理器和操作系统中,它的确切工作方式无论如何都是非常抽象的,所以通常不需要担心内存如何工作,除了(在它允许的语言中),你不能使用内存你还没有分配或释放你已经释放的内存。
是什么让一个更快?
堆栈更快,因为所有空闲内存总是连续的。 没有列表需要维护所有可用内存段,只有一个指向当前栈顶的指针。 为了这个目的,编译器通常将这个指针存储在一个特殊的快速寄存器中。 更重要的是,堆栈中的后续操作通常集中在非常接近的内存区域,这在很低的水平上适合处理器内存缓存的优化。