【问题标题】:Why did heap memory come into existence?为什么堆内存会出现?
【发布时间】:2015-12-07 12:15:15
【问题描述】:

我的问题非常基本,正如标题所说,我想知道为什么堆内存会出现。我知道堆内存支持动态内存分配,但是当两种类型的内存(静态和堆)都存在于 RAM 上时,为什么不能通过/在堆栈内存中完成相同的操作。为什么需要在进程地址空间中拥有单独的内存段。在发布这个问题时,我遇到了几篇与堆内存主题相关的帖子,但到目前为止,我检查并没有回答我的问题,而是更多关于堆内存的定义和行为. 为了更清楚地说明我的问题,为什么不能有一个大的堆栈内存也可以处理动态内存分配。我们知道动态内存是动态创建的,可以在需要时释放。也可以是动态内存分配的内存溢出或泄漏问题,但如果有一个大的堆栈内存作为堆或堆栈,如果内存没有被释放,则无法完成'那么即使在程序退出的情况下,它也肯定会导致浪费.话虽如此,当两个内存都驻留在 RAM 上时,为什么需要不同的内存结构来处理内存。

【问题讨论】:

  • 你在问为什么动态内存分配是使用堆结构来完成的?或者您是在问为什么会有动态分配?
  • 我想他指的是第二个问题,为什么要动态分配
  • 不,我认为他指的是第一个。堆栈空间的大小不是固定的吗?如果你的结构是固定大小的,那么动态内存就不能很好地工作。如果你把堆栈做得很大以适应动态分配,就会浪费很多空间。
  • @AnastasiyaAsadullayeva 我使用的一个主要代码库不使用动态分配。它用于无法接受内存不足错误的关键应用程序。
  • @theartist33 关于堆栈的要点是它严格后进先出。动态内存不能那样工作。对两个结构的需求是非常基本的。它可以追溯到人们用汇编语言编写的时候,为了执行一个子例程,你需要 CPU 在某个地方转储其工作寄存器并在调用返回后再次获取它们。但是程序正在处理的主要数据需要存储在其他地方,这样它就不会在调用/返回堆栈转储过程中被破坏。

标签: c++ c memory


【解决方案1】:

对于堆栈分配,分配对象的生命周期有严格的 LIFO(后进先出)策略。也就是说,如果你分配对象 A,然后是对象 B,然后是对象 C,它们总是会以相反的顺序被释放。

这在很多情况下都很有用,但在某些情况下,例如希望对象 C 在 B 和 A 被销毁后继续存在。使用堆栈无法做到这一点,因此为了处理这些情况,引入了堆。它比堆栈分配更难正确使用,但也更灵活/强大。

【讨论】:

  • 感谢大家抽空回复并提供相关信息。对于这个问题,我几乎通过不同的帖子得到了答案。许多答案都说了同样的话,但我坚持上面的答案。再次感谢!!
【解决方案2】:

内存管理,包括类似堆的分配和内存释放可以追溯到 1950 年代。 “为什么”是因为需要它。

请注意,IBM 360 及更高版本的大型机系列没有堆栈。相反,调用者为寄存器提供了保存区域,而简单的程序只是为该保存区域使用了一个静态数组。更高级的程序可以根据需要动态分配内存(取决于操作系统),因此在这种情况下,堆内存存在而没有堆栈。

如果您想知道,中断是通过成对的词向量来处理的,一个用于存储机器的当前状态,另一个在某个时间预先用状态初始化以处理该特定中断。

其他较旧的大型机和小型机没有堆栈。在某些情况下,类似于 IBM 360 中断处理程序,函数的第一个字用于存储返回地址,而实际代码以第二个字开头并通过第一个字间接跳转完成返回(例如 CDC 3150,惠普 2100)。

【讨论】:

    【解决方案3】:

    通常堆栈和堆存在于 RAM 中的一大块内存中,其中堆从开头开始向前增长,而堆栈从 RAM 顶部开始向后增长。中间的空闲内存可以分配给堆栈或堆,具体取决于运行时需要哪一个。 堆栈和堆之间的主要区别在于如何执行分配。栈需要内存管理的后进先出模型,而堆使用动态内存分配,更灵活但成本更高。

    许多有用的编程模式无法用成本较低的堆栈内存模型来表示。作为一个主要示例,可增长数组(C++ 中的向量)的内存不能存储在堆栈中,因为所需的内存量在编译时是未知的,并且将来可能会增加,需要重新分配。

    【讨论】:

    • 你并没有真正回答这个问题。
    • 那么我可能误解了这个问题。从我阅读问题的方式来看,他似乎熟悉堆上动态内存分配的概念,但想知道为什么堆栈和堆占用了两个不同的内存部分。我的回答是它们共享一个共同的记忆部分。
    • 我认为答案确实解决了问题所在。
    【解决方案4】:

    您的问题的非常简单的答案是每种内存类型(堆栈和堆)必须由不同的机制管理。栈内存基本上是由编译器管理的,一般是由编译器根据你代码中的栈需求生成的代码来管理的。堆栈分配在为此目的预留的内存空间上推送和弹出。堆内存来来去去,分配的内存量可以在同一组代码的每次传递中发生变化,因此在编译时不知道空间需求。

    从计算机系统的角度来看,内存就是内存。我见过嵌入式系统,其中嵌入式操作系统从堆中分配一大块连续的内存区域,并将堆栈指针指向该区域。因此,堆栈内存实际上是在堆空间中,并且是由堆管理器管理的堆内存的单个分配。请注意,在系统运行期间永远不会释放内存。

    所以简短的回答是堆栈和堆内存有不同的要求,因此必须以不同的方式管理每种“类型”的内存以满足这些要求。

    【讨论】:

      【解决方案5】:

      你了解堆栈的实际工作原理吗?

      调用函数时,会在堆栈上为其局部变量分配内存。当函数返回时,堆栈返回,堆栈指针回到函数调用之前的位置。当调用下一个函数时,该函数在堆栈上分配的所有内容都将被覆盖。

      如果没有堆,您将无法让对象或数组比创建它的函数调用更有效。

      此外,堆栈的部分优势在于内存的局部性。您的所有局部变量在内存中都非常接近,因此将位于同一内存页面上。将大量的大型对象分配转储到堆栈上会杀死该位置。

      【讨论】:

      • 但是您可以从函数中返回对象(编译器可以省略副本。)因此您可以有效地使对象比创建它们的函数更有效。
      • 不——你只会返回对象的浅拷贝。分配给对象持有的指针的任何内存都将被覆盖,因为它也在堆栈上。
      • @juanchopanza 返回对象的函数只能这样做,因为该对象是在堆上分配的。
      • 他说的是按值返回函数,这在 C++ 中是可以的——技术上——但它会遇到上述问题。
      • @DarkSquirtings 当然,您也可以在 C 中按值返回。你关于只返回浅对象的观点是无关的。您不必为了返回它而动态分配东西。
      【解决方案6】:

      堆和栈的区别有点随意。从概念上讲,栈内存只是用内存来表示函数调用栈中函数的状态。堆内存具有函数调用堆栈中函数之外的范围。理论上,这两者都可以在程序中占用相同的内存部分。然而,实际上它们并没有,因为硬件和操作系统是专门为这两种不同的用途而设计的。

      理论上,堆不是必需的。一旦对象的范围不是函数调用框架,管理对象生命周期和内存就变得很困难。当函数退出时,对象的所有权必须在调用堆栈中传递到更高的位置,这通常意味着复制。因此,这将意味着严重的性能损失。

      其他问题之一是最终需要回收对象。由于对象在堆栈内存中是连续布局的,因此死对象和活动对象之间的内存中会存在各种大小的间隙。从本质上讲,这意味着死内存无法使用和泄漏。

      因此,的主要原因是为了有效地维护函数调用范围之外的内存。

      【讨论】:

        【解决方案7】:

        堆栈内存比堆内存快,但以灵活性为代价。

        这就像在磁盘上创建文件。您可以创建大量占用大量空间的文件。稍后您可以删除其中的一些。部分空间再次空闲,可供其他进程使用。这种机制非常有用,尽管有时您会遇到碎片问题。您可以将堆分配视为类似的事情。相比之下堆栈内存通常要小得多,管理也很简单(只是移动一个堆栈指针),不能像堆那样操作。

        你可以说堆栈和堆是 CPU 和操作系统设计所需要的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-06-18
          • 2018-06-02
          相关资源
          最近更新 更多