堆内存与栈内存

120

可能是重复问题:
堆和栈分别是什么?它们在哪里?

我在使用C++编程时经常会想知道栈内存与堆内存的区别。我只知道当调用new时,我可以从堆中获得内存。如果创建局部变量,我可以从栈中获得内存。在互联网上进行了一些研究之后,最常见的答案是栈内存是临时的,堆内存是永久的。

栈和堆内存模型是操作系统还是计算机体系结构的概念?因此,其中一些可能不遵循栈和堆内存模型,或者它们都遵循它?

栈和堆内存是虚拟内存的内存模型抽象(可能会在磁盘和RAM之间交换内存)。因此,栈和堆内存物理上可能是RAM或磁盘?那么为什么堆分配似乎比栈分配要慢呢?

此外,主程序会在栈中还是堆中运行?

另外,如果进程的栈内存或堆内存耗尽会发生什么?

谢谢


6
这确实引发了一些问题,这些问题在你链接的问题中没有提到。 - Maxpm
3个回答

78

C++中,栈内存是局部变量存储/构建的地方。栈也用于保存传递给函数的参数。

栈非常类似于std::stack类:将参数推入其中,然后调用函数。然后函数知道它期望的参数可以在栈的末尾找到。同样,函数可以将局部变量推入栈中,在从函数返回之前将其弹出。(注意-编译器优化和调用约定都意味着事情并不这么简单)

栈最好从低层次理解,我建议《汇编艺术-在堆栈上传递参数》.很少有情况需要考虑从C++进行任何手动堆栈操作。

一般来说,栈是首选的,因为它通常在CPU缓存中,所以涉及其中存储的对象的操作往往更快。但是栈是有限资源,不应该用于任何大型任务。耗尽栈内存称为堆栈缓冲区溢出。这是一个严重的问题,但除非你有疯狂的递归函数或类似的东西,否则你不会遇到它。

堆内存与rskar所说的差不多。一般来说,使用new分配的C++对象,或使用malloc之类分配的内存块都在堆上。堆内存几乎总是必须手动释放,尽管您应该使用智能指针类或类似方法,以避免需要记住这样做。耗尽堆内存可能(会?)导致std::bad_alloc。


26
“Stack memory almost always must be manually freed.” 的意思是栈内存几乎总是需要手动释放。你想表达的应该是堆内存。另外,值得一提的是,堆内存是“全局”的,当你用尽了堆内存时,其他应用程序也将用尽堆内存。但是,栈具有局部作用域。 - Spidey

38

栈内存是特定的内存范围,可以通过CPU的栈寄存器访问。在汇编语言中,栈被用作实现“跳转-子程序”-“返回”代码模式的一种方式,同时也用于实现硬件级别的中断处理。例如,在中断期间,栈被用于存储各种CPU寄存器,包括状态(表示操作结果)和程序计数器(当中断发生时,CPU在程序中的位置)。

栈内存非常符合通常的CPU设计。其分配/释放速度很快,因为它严格按照后进先出的设计。这只是一个在栈寄存器上进行移动操作和递减/递增操作的简单事情。

堆内存仅仅是在程序加载并分配栈内存后剩下来的内存。它可能(或可能不)包括全局变量空间(这是一种约定俗成的做法)。

现代抢占式多任务操作系统带有虚拟内存和内存映射设备,使得实际情况更加复杂,但这就是栈和堆的主要区别。


4
这个观点有很多错误。就我所知,通常来说,“堆栈内存”和“堆内存”没有任何区别,不管是在哪种架构上。它们都可以通过堆栈指针、索引寄存器或其他方式进行访问。堆栈指针和索引寄存器都可以随意更改以访问进程可用的任何内存。 - user2100815
@unapersson:RAM 就是 RAM,SP 当然可以更改和分配。"堆栈内存" 更多的是意图或角色的术语,不一定是专门的硬件部件。顺便说一下,在 6502 的情况下,你的堆栈确实被固定在第一页(字节 256 到 511)。 - rskar
@rskar 是的,确实如此。我在1984年用6502汇编语言在BBC Micro上编写了KERMIT协议的实现,并进行了完整的VT100终端仿真(对于一个Z80程序员来说并不是一次愉快的经历),所以我知道我在这里说的是什么。 - user2100815
@unapersson:好的,那么 - 我到底说错了什么? - rskar
6
对于大多数体系结构来说,堆栈内存和普通内存之间没有区别。即使在6502上,您也可以(有时必须)在“普通”内存中实现自己的堆栈-只是不能通过调用和返回操作码来使用它。即使在6502上,堆栈内存也没有什么特殊之处,只有如何访问它(通过6502堆栈页)。您似乎在暗示堆栈和其他类型的内存实际上存在物理差异。 - user2100815
10
抱歉,我从未意图暗示堆栈和其他类型的内存之间实际上存在物理差异。 - rskar

3

这是一种语言抽象 - 有些语言都有,有些只有一个,有些则没有。

对于C++而言,代码不会运行在堆栈或堆内存中。您可以通过重复调用new来分配内存并在循环中不调用delete来释放它来测试如果内存不足时会发生什么。 但在执行此操作之前,请备份系统


65
在尝试填充堆之前,请进行“系统备份”?你使用的平台是MS-DOS吗? - Fred Foo
4
但是,你是否已经取消了用户进程的限制?并且你尝试在Windows上运行了吗?对于我们大多数Unix人来说,“必须重新启动”的下一步就是被解雇。 - user2100815
6
@unapersson说:所有系统(Linux、Mac OS X、OpenBSD)都有默认的限制。不过,我从来没有在Windows上尝试过任何重型操作,因为我讨厌清理破碎的玻璃 ;) - Fred Foo
2
@unapersson:啊,对了。我不习惯在生产服务器上运行这样的测试,并且我相信我的本地计算中心不会取消HPC集群上的限制 :) - Fred Foo
2
@TonyK 我猜这是真实世界(事情会出错,如果出错你就得付出代价),而不是你所生活的幸福美好的世界。 - user2100815
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接