堆栈和堆内存的大小[重复]
Posted
技术标签:
【中文标题】堆栈和堆内存的大小[重复]【英文标题】:Size of stack and heap memory [duplicate] 【发布时间】:2012-09-23 03:05:05 【问题描述】:可能重复:What and where are the stack and heap?
关于c程序中内存布局的基本概念,我的理解是:
该语言使用两种主要的数据结构 stack 和 heap。 创建堆栈来存储子程序的局部变量和簿记数据 创建堆来存储程序动态分配的变量 堆本质上是可变长度的。(在堆栈上不太确定) 通常编译器/语言负责在执行前请求操作系统创建这些数据结构。问题
创建堆栈/堆的初始大小是多少?谁来决定? 它们是在哪里创建的物理内存?我看到一般描述为 “堆栈是在***地址创建的,而堆是在低级地址创建的” 请详细说明【问题讨论】:
【参考方案1】:"栈是在顶层地址和堆中创建的 低级地址”请详细说明
这是一个神话。它可能有历史真相的基础。它有时可能会与您在现实生活中看到的事物产生共鸣。但事实并非如此。
不过,探索起来很容易:
#include <stdlib.h>
#include <stdio.h>
void check(int depth)
char c;
char *ptr = malloc(1);
printf("stack at %p, heap at %p\n", &c, ptr);
if (depth <= 0) return;
check(depth-1);
int main()
check(10);
return 0;
在我的机器上我看到了:
stack at 0x22ac3b, heap at 0x20010240
stack at 0x22ac0b, heap at 0x200485b0
stack at 0x22abdb, heap at 0x200485c0
stack at 0x22abab, heap at 0x200485d0
stack at 0x22ab7b, heap at 0x200485e0
stack at 0x22ab4b, heap at 0x200485f0
stack at 0x22ab1b, heap at 0x20048600
stack at 0x22aaeb, heap at 0x20048610
stack at 0x22aabb, heap at 0x20048620
stack at 0x22aa8b, heap at 0x20048630
stack at 0x22aa5b, heap at 0x20048640
因此,堆栈向下,堆向上(正如您根据神话所期望的那样),但堆栈的地址更小,并且它们没有朝着彼此增长(神话破灭)。
顺便说一句,我的check
函数是尾递归的,在某些带有一些编译器选项的实现中,您可能会看到堆栈根本没有移动。这告诉您为什么该标准不强制要求所有这些工作方式 - 如果这样做可能会无意中禁止有用的优化。
【讨论】:
尾递归在递归调用之前消除当前堆栈帧,因为在递归调用结束后不需要它。栈还在往下走,只是在这种情况下发生,不需要增加栈。 这不是神话。您在现代非ASLR Linux 上的程序输出:stack at 0x7fff356d5fd7, heap at 0x1d39010
。执行sudo bash -c 'for x in /proc/*/maps; do echo $x; egrep stack\|heap $x; done'
显示所有进程都类似。 2014 年使用堆栈/堆冲突作为漏洞利用:"The effect of all these command line arguments is to bloat both the stack (which grows down) and the heap (which grows up) until they crash into each other."
存在一个声明为真的系统这一事实并不能阻止它在作为问题中的“一般描述”呈现时成为神话。这是一种方法,但一般声称它是“它的完成方式”是错误的。【参考方案2】:
如前所述,大小是特定于操作系统的。例如在使用 Visual Studio 的 Windows 上,默认堆栈大小为 1MB
msdn
在 Linux 上,以下命令可以显示你当前的命令。
ulimit -s or -a
在我的 Linux mint 64 位上,它显示 8192 KB。
加载到内存中的每个程序都有几个段。在汇编中,可以使用 .data、.code 等前缀 (intelx86) 来指示其中的每一个。
它是一个数据段,它有几个子部分。除了其他几个之外,堆栈和堆都是它的一部分。
堆栈也可以隐式增长,即当您进行另一个函数调用时,激活记录被推送到堆栈,它们通过利用更多的堆栈内存来实现。这就是为什么当程序用完分配的堆栈时,无限递归会导致崩溃。
当函数调用返回时,该激活记录被弹出并且堆栈收缩。
相反,堆从相反的方向增长并包含所有动态分配的内存。
这两个段反向增长的原因是为了最大限度地利用它们的组合内存。请注意,正如 cmets 中所述,这不是 c 标准,但大多数常见的操作系统都已实现。
------栈开始---------栈向下增长
-------- 除非它们相互交叉,否则程序可以运行。
-------堆开始------------堆向上增长
如果您的程序不使用堆,您的堆栈也可以使用最大内存,包括堆的内存。如果程序很少进行递归调用并使用最少的局部变量(即堆栈使用较少的内存),它可以最大限度地利用堆。
数据段的其他部分是BSS等,可能包含未初始化的静态变量等字段
【讨论】:
C 标准没有指定栈/堆扩展的方向。他们都可以成长或下降,这是标准允许的。 同意。这是一个常见的实现,例如在 Linux 中。但就像提到的那样,不是 c 标准 感谢您的回答!!此外,我想引用以下“ulimit”确实显示了堆栈大小的上限(不是编译器使用的上限)。我假设在这个限制之后程序终止并出现堆栈溢出错误【参考方案3】:创建堆栈/堆的初始大小是多少?谁来决定?
这是特定于编译器和操作系统的。
它们是在哪里创建的物理内存?我看到一般描述为“在顶层地址创建堆,在低层地址创建堆栈”。
这是特定于编译器和操作系统的。
真的。语言标准没有规定最小堆栈大小,也没有指定堆栈或堆在内存中的位置。这样做的原因是使 C 程序更少依赖这些细节,因此更容易移植到不同的平台(阅读:不同的操作系统、不同的 CPU、不同的编译器)。
【讨论】:
【参考方案4】:首先,C 标准对栈/堆的实现方式没有任何要求 该平台。
What is the initial size with which a stack/heap is created? and who decides it?
通常,操作系统会为每个进程分配一个固定大小的 stack,这是特定于平台的。 堆大小没有限制,程序通常拥有所有可用的虚拟地址空间。
Wherein physical memory are they are created?
这是特定于平台的。通常栈向下增长,堆向上增长。
【讨论】:
以上是关于堆栈和堆内存的大小[重复]的主要内容,如果未能解决你的问题,请参考以下文章