为啥要在堆上而不是栈上分配内存? [复制]

Posted

技术标签:

【中文标题】为啥要在堆上而不是栈上分配内存? [复制]【英文标题】:Why would you ever want to allocate memory on the heap rather than the stack? [duplicate]为什么要在堆上而不是栈上分配内存? [复制] 【发布时间】:2010-12-05 17:12:07 【问题描述】:

可能重复:When is it best to use a Stack instead of a Heap and vice versa?

我已经阅读了一些关于堆与堆栈的其他问题,但它们似乎更关注堆/堆栈的作用,而不是您为什么要使用它们。

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

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

【问题讨论】:

复制:***.com/questions/102009/… @Naveen:Sorta - 另一个是 C++ 问题,这是 C,所以一些答案讨论了不适用于 C 的“删除”和“新”。 【参考方案1】:

堆栈和堆共享相同的“开放”内存空间,如果您使用整个内存段,最终将不得不到达它们相遇的点。保持它们每个人使用的空间之间的平衡将在以后分配和取消分配内存的摊销成本中获得较小的渐近值。

【讨论】:

【参考方案2】:

堆栈变量(通常称为“自动变量”)最适合用于您希望始终保持不变且始终很小的事物。

int x;
char foo[32];

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

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

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

int readdata ( FILE * f ) 
  char inputdata[50*1024*1025];
  ...
  return x;

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

【讨论】:

【参考方案3】:

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

运行时控制对象的大小(在程序执行期间,包括初始大小和“后期”大小)。

例如,您可以分配一个特定大小的数组,该数组只有在运行时才知道。

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

运行时控制对象的总数。

例如,当你构建一个二叉树结构时,你不能提前在堆栈上有意义地分配树的节点。您必须使用堆来“按需”分配它们。

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

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

【讨论】:

【参考方案4】:

只是为了添加 您可以使用alloca 在堆栈上分配内存,但堆栈上的内存也是有限的,而且空间仅在函数执行期间存在。 这并不意味着所有东西都应该在堆上分配。像所有design decisions 一样,这也有点困难,应该使用两者的“明智”组合。

【讨论】:

我必须再次声明“函数x() 是一个非标准函数”。 alloca() 在许多平台上以某种形式出现,但不是任何标准的一部分。 Chris:这是绝对正确的,但是在更广泛地讨论堆与堆栈分配的背景下,alloca() 在某些地方的存在值得考虑。特别是因为它清楚地表明,对分配大小的运行时计算的限制是 C 强加的限制,而不是基于堆栈的分配思想所固有的东西。【参考方案5】:
    您希望分配在函数调用之外存在 您希望节省堆栈空间(通常限制为几 MB) 您正在使用可重新定位的内存(Win16、数据库等),或者想要从分配失败中恢复。 可变长度任何东西。你可以伪造这个,但你的代码会很讨厌。

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

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

所以我想做一个字符串。我可以在堆上或堆栈上制作它。让我们都尝试一下:

char *heap = malloc(14);
if(heap == NULL)
  
    // bad things happened!
  
strcat(heap, "Hello, world!");

对于堆栈:

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

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

char *tmp = realloc(heap, 20);
if(tmp == NULL)
  
    // bad things happened!
  
heap = tmp;
memmove(heap + 13, heap + 7);
memcpy(heap + 7, "cruel ", 6);

对于堆栈:

// umm... What?

这只是一个好处,其他人也提到了其他好处,但这是一个相当不错的好处。有了堆,我们至少可以尝试让我们分配的空间更大。有了堆栈,我们就被我们所拥有的东西困住了。如果我们想要增长空间,我们必须提前声明,而且我们都知道看到这个很臭:

char username[MAX_BUF_SIZE];

【讨论】:

【参考方案8】:

有几个原因:

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

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

【讨论】:

Stack space is typically a more limited resource than heap space - 为什么? @onepiece:它主要归结为默认进程布局选择和堆栈限制,但还有一个基本事实,即进程中的每个线程都需要为其堆栈分配一个连续的虚拟内存块.【参考方案9】:

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

【讨论】:

以上是关于为啥要在堆上而不是栈上分配内存? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的对象似乎在堆上而不使用`new`?

go的值类型和引用类型2——内存分配规则

动态内存使用速度较慢是啥? [复制]

jvmiz9是什么意思

java语言中,类的成员变量分配在哪个内存区?

引用类型值类型 理解