关于C#:为什么你想要在堆而不是堆栈上分配内存?

Why would you ever want to allocate memory on the heap rather than the stack?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
When is it best to use a Stack instead of a Heap and vice versa?


我已经阅读了关于堆与堆栈的其他几个问题,但它们似乎更关注堆/堆栈的作用而不是使用它们的原因。


在我看来,堆栈分配几乎总是首选,因为它更快(只需移动堆栈指针与在堆中查找可用空间),并且您在使用它时不必手动释放已分配的内存。 我可以看到使用堆分配的唯一原因是,如果您想在函数中创建一个对象,然后在该函数作用域之外使用它,因为堆栈分配的内存在从函数返回后自动取消分配。


还有其他原因使用堆分配而不是我不知道的堆栈分配吗?



有几个原因:

  • 主要的是通过堆分配,您可以最灵活地控制对象的生命周期(从malloc / callocfree);
  • 堆栈空间通常是比堆空间更有限的资源,至少在默认配置中是这样;
  • 可以优雅地处理分配堆空间的失败,而堆栈空间的耗尽通常是不可恢复的。


如果没有灵活的对象生命周期,那么有用的数据结构(如二叉树和链表)几乎不可能编写。


  • 您希望分配超出函数调用的范围
  • 您希望节省堆栈空间(通常限制为几MB)
  • 您正在使用可重新定位的内存(Win16,数据库等),或者想要从分配失败中恢复。
  • 可变长度。你可以假装这个,但你的代码真的很讨厌。

  • 最重要的是#1。一旦你进入任何类型的并发或IPC#1到处都是。即使大多数非平凡的单线程应用程序在没有一些堆分配的情况下设计也很棘手。这实际上是伪造C / C ++中的函数式语言。



    所以我想做一个字符串。我可以在堆上或堆栈上创建它。我们试试两个:

    1
    2
    3
    4
    5
    6
    char *heap = malloc(14);
    if(heap == NULL)
      {
        // bad things happened!
      }
    strcat(heap,"Hello, world!");


    对于堆栈:

    1
    char stack[] ="Hello, world!";


    所以现在我在各自的位置都有这两个字符串。后来,我想让它们更长一些:

    1
    2
    3
    4
    5
    6
    7
    8
    char *tmp = realloc(heap, 20);
    if(tmp == NULL)
      {
        // bad things happened!
      }
    heap = tmp;
    memmove(heap + 13, heap + 7);
    memcpy(heap + 7,"cruel", 6);


    对于堆栈:

    1
    // umm... What?


    这只是一个好处,其他人已经提到了其他好处,但这是一个相当不错的好处。使用堆,我们至少可以尝试使分配的空间更大。随着堆栈,我们坚持我们拥有的东西。如果我们想要增长的空间,我们必须预先宣布它,我们都知道如何看到它:

    1
    char username[MAX_BUF_SIZE];


    使用堆的最明显的理由是当你调用一个函数并且需要返回未知长度的东西时。有时调用者可以将存储块和大小传递给函数,但是在其他时候这是不切实际的,特别是如果返回的东西是复杂的(例如,指针飞来飞去的不同对象的集合等)。



    在很多情况下,尺寸限制是一个巨大的破坏者。堆栈通常以低兆字节甚至千字节(对于堆栈中的所有内容)进行测量,而所有现代PC都允许您使用几千兆字节的堆。因此,如果您要使用大量数据,则绝对需要堆。



    堆栈和堆共享相同的"开放"内存空间,如果使用整个内存段,它们必须最终达到它们相遇的程度。保持每个人使用的空间之间的平衡将在以后分配和分配内存的成本进行摊销成为较小的渐近值。



    堆栈变量(通常称为"自动变量")最适用于您希望始终相同的事物,并且始终很小。

    1
    2
    int x;
    char foo[32];


    是否所有堆栈分配,这些在编译时也是固定的。


    堆分配的最佳原因是您无法始终知道需要多少空间。一旦程序运行,您通常只知道这一点。您可能对限制有所了解,但您只想使用所需的确切空间量。


    如果你必须阅读一个可能是1k到50mb的文件,你就不会这样做: -

    1
    2
    3
    4
    5
    int readdata ( FILE * f ) {
      char inputdata[50*1024*1025];
      ...
      return x;
    }


    这将尝试在堆栈上分配50mb,这通常会失败,因为堆栈通常限制为256k。



    除了手动控制对象的生命周期(你提到过)之外,使用堆的其他原因包括:

    • 运行时控制对象的大小(在程序执行期间,初始大小和"稍后"大小)。


    例如,您可以分配一定大小的数组,该数组仅在运行时已知。


    随着在C99中引入VLA(可变长度数组),可以在不使用堆的情况下分配固定运行时大小的数组(这基本上是'alloca'功能的语言级实现)。但是,在其他情况下,即使在C99中,您仍然需要堆。

    • 运行时控制对象总数。


    例如,在构建二叉树结构时,您无法事先在堆栈上有意义地分配树的节点。您必须使用堆"按需"分配它们。

    • 低级技术考虑,因为有限的堆栈空间(其他人已经提到过)。


    当你需要一个较大的I / O缓冲区时,即使是很短的时间(在单个函数内),从堆中请求它而不是声明一个大的自动数组更有意义。



    只是为了补充
    您可以使用alloca在堆栈上分配内存,但堆栈上的内存也是有限的,并且该空间仅在函数执行期间存在。
    这并不意味着应该在堆上分配所有内容。像所有设计决策一样,这也有点困难,应该使用两者的"明智"组合。