堆栈和堆在哪里?

2024-04-20 01:22:43 发布

您现在位置:Python中文网/ 问答频道 /正文


Tags: python
3条回答

堆栈:

  • 像堆一样存储在计算机内存中。
  • 在堆栈上创建的变量将超出范围并自动释放。
  • 与堆上的变量相比,分配要快得多。
  • 用实际的堆栈数据结构实现。
  • 存储本地数据,返回地址,用于参数传递。
  • 当使用太多堆栈时(主要来自无限或太深的递归,非常大的分配),可能会出现堆栈溢出。
  • 在堆栈上创建的数据可以在没有指针的情况下使用。
  • 如果您确切知道在编译之前需要分配多少数据,并且它不太大,那么您可以使用堆栈。
  • 通常在程序启动时已确定最大大小。

堆:

  • 像堆栈一样存储在计算机内存中。
  • 在C++中,堆上的变量必须手动销毁,并且永远不会超出范围。使用deletedelete[]free释放数据。
  • 与堆栈上的变量相比,分配较慢。
  • 按需分配数据块以供程序使用。
  • 当存在大量分配和释放时,可能会有碎片。
  • 在C++或C中,堆上创建的数据将由指针指向,并分别用^ {CD4}}或^ {CD5}}分配。
  • 如果请求分配的缓冲区太大,则可能会发生分配失败。
  • 如果不知道在运行时需要多少数据,或者需要分配大量数据,则可以使用堆。
  • 对内存泄漏负责。

示例:

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;

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

  • 在一堆物品中,物品按照放置的顺序一个接一个地放在上面,你只能把上面的一个拿走(不要把整个东西都掀翻)。

    Stack like a stack of papers

    堆栈的简单性在于,您不需要维护包含已分配内存的每个部分的记录的表;您需要的唯一状态信息是指向堆栈末尾的一个指针。要分配和取消分配,只需增加和减少单个指针。注意:堆栈有时可以实现为从一段内存的顶部开始向下扩展,而不是向上扩展。

  • 在堆中,项目的放置方式没有特定的顺序。您可以按任何顺序进入并移除项目,因为没有清晰的“顶部”项目。

    Heap like a heap of licorice allsorts

    堆分配需要维护一个完整的记录,记录分配的内存和没有分配的内存,以及一些开销维护,以减少碎片,找到足够大的连续内存段以适应请求的大小,等等。内存可以在任何时候释放,留下空闲空间。有时,内存分配器将执行维护任务,例如通过移动已分配的内存对内存进行碎片整理,或者在运行时进行垃圾收集-当内存不再在作用域中时进行标识并将其释放。

这些图像应该能够很好地描述在堆栈和堆中分配和释放内存的两种方式。好极了!

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

    如前所述,堆和堆栈是通用术语,可以通过多种方式实现。计算机程序通常有一个名为call stack的堆栈,该堆栈存储与当前函数相关的信息,例如指向从哪个函数调用它的指针,以及任何局部变量。因为函数调用其他函数,然后返回,所以堆栈会增大和缩小以保存调用堆栈下函数的信息。一个程序并没有真正的运行时控制权;它是由编程语言、操作系统甚至系统架构决定的。

    堆是一个通用术语,用于动态和随机分配的任何内存;即无序。内存通常由操作系统分配,应用程序调用API函数来进行分配。在管理动态分配的内存(通常由操作系统处理)时,需要相当一部分开销。

  • 他们的范围是什么?

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

  • 是什么决定了他们每个人?

    同样,它取决于语言、编译器、操作系统和体系结构。堆栈通常是预先分配的,因为根据定义,它必须是连续内存(在最后一段中有更多内容)。语言编译器或操作系统决定其大小。您不会在堆栈上存储大量数据,因此它将足够大,永远不应完全使用,除非出现不需要的无休止递归(因此称为“堆栈溢出”)或其他不寻常的编程决策。

    堆是可以动态分配的任何东西的通用术语。根据你看它的方式,它的大小是不断变化的。在现代处理器和操作系统中,它的确切工作方式无论如何都是非常抽象的,所以您通常不需要太担心它的深层工作方式,除非(在它允许您使用的语言中)您不能使用尚未分配的内存或已释放的内存。

  • 是什么让你更快?

    堆栈更快,因为所有可用内存总是连续的。不需要维护所有空闲内存段的列表,只需一个指向当前堆栈顶部的指针。为此,编译器通常将此指针存储在一个特殊的、快速的register中。更重要的是,堆栈上的后续操作通常集中在内存的非常近的区域内,这在非常低的级别上有利于处理器在片缓存上进行优化。

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

堆是为动态分配而预留的内存。与堆栈不同,没有强制模式来分配和释放堆中的块;您可以随时分配块并释放它。这使得跟踪在任何给定时间分配或释放堆的哪些部分变得更加复杂;有许多自定义堆分配器可用于针对不同的使用模式优化堆性能。

每个线程都有一个堆栈,而应用程序通常只有一个堆(尽管对于不同类型的分配有多个堆并不少见)。

要直接回答您的问题:

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

创建线程时,操作系统为每个系统级线程分配堆栈。通常,操作系统由语言运行时调用以为应用程序分配堆。

What is their scope?

堆栈附加到线程,因此当线程退出堆栈时,将回收堆栈。堆通常由运行时在应用程序启动时分配,并在应用程序(技术上是进程)退出时回收。

What determines the size of each of them?

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

What makes one faster?

堆栈的速度更快,因为访问模式使得从中分配和释放内存变得很简单(指针/整数只是递增或递减),而堆在分配或释放中有更复杂的簿记。此外,堆栈中的每个字节往往被非常频繁地重用,这意味着它往往被映射到处理器的缓存,从而使它非常快。堆的另一个性能冲击是,堆(主要是全局资源)通常必须是多线程安全的,即每个分配和释放都需要与程序中的“所有”其他堆访问同步。

一个清晰的演示:
图像源:vikashazrati.wordpress.com

相关问题 更多 >