我一直在思考这个问题,但还没有找到任何解释。
哪个模块决定应该为 C++ 程序分配多少内存?是操作系统决定编译器的推荐吗?..链接器?
分配的内存中堆栈和堆的比例是多少?
我一直在思考这个问题,但还没有找到任何解释。
哪个模块决定应该为 C++ 程序分配多少内存?是操作系统决定编译器的推荐吗?..链接器?
分配的内存中堆栈和堆的比例是多少?
对于不同的操作系统,答案是不同的。通常,可执行文件包含主线程所需的堆栈大小,由链接器放置在那里,可能会被操作系统设置覆盖。操作系统设置可以通过一种或多种方式配置,可能针对每个用户。一些操作系统不需要预先指定堆栈大小,它们可以在使用堆栈时或多或少无限期地添加堆栈(直到达到硬限制或系统耗尽可用内存)。那些确实需要预先确定大小的人可能最初只分配地址空间而不是内存,并且在堆栈达到那个距离时将地址映射到内存。
堆通常不会预先分配,因此没有“堆栈和堆的比例”。分配给进程的总内存可能会或可能不会受到限制——如果没有,那么它可以达到系统资源允许的上限,或者在 32 位系统上可能会受到可用地址空间的限制。
这不是 C++ 标准中的问题。它取决于编译器和操作系统。
有关链接器产生的那种事情的例子,操作系统在确定程序请求的资源时会考虑这些事情,请参阅:
http://en.wikipedia.org/wiki/Executable_and_Linkable_Format#ELF_file_layout
在某些情况下,有一些 API 可以专门从操作系统请求资源:
在使用 GNU 编译器编译期间更改 Linux 中 C++ 应用程序的堆栈大小
还有一些方法可以告诉操作系统在某些环境中设置配额和限制:
https://stackoverflow.com/questions/4983120/limit-memory-usage-for-a-single-linux-process
如果您想对某个操作系统如何管理资源使用进行实证研究,您可能会使用进程监视器实用程序来更好地了解它,而不是寻找文档......特别是。使用闭源操作系统。
取决于您的程序和操作系统。通常,在启动时只分配足够的内存来保存可执行文件、任何只读数据,通常大约 4k 用于堆栈。然后,当您调用 malloc 或 new 来分配内存时,您将获得虚拟内存空间,而无需任何物理内存对其进行备份。这称为延迟分配,只有在您实际写入时才会物理分配内存。
编译并计时以下内容以了解我在说什么:
//justwrites.c
#include <stdlib.h>
int main(int argc, char **argv) {
int *big = calloc(sizeof(int),19531); // number of writes
return 0;
}
// deadbeef.c
#include <stdlib.h>
int main(int argc, char **argv) {
int *big = malloc(sizeof(int)*20000000); // allocate 8 million bytes
// immediately write to each page to simulate all at once allocation
// assuming 4k page size on 32bit machine
for ( int* end = big + 20000000; big < end; big+=1024 ) *big = 0xDEADBEEF ;
return 0;
}
// bigmalloc.c
#include <stdlib.h>
int main(int argc, char **argv) {
int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes
return 0;
}
至少对于 32 位 Windows,每个进程都有自己的地址空间副本,2G 用户 2G 内核(由所有进程共享),虚拟内存子系统确保访问同一位置的进程获得其进程的适当数据。这就是一个程序可以具有相同的入口点并多次运行而不踩踏具有相同可执行文件的其他进程正在使用的数据的方式。
应用程序将继续使用更多的虚拟内存,内核将为该进程分配更多的物理内存,直到用完物理内存、交换空间/分页文件。您可以通过系统调用限制进程可以使用的内存。
堆栈和堆几乎总是分配在可用内存的两端,因此堆栈从可用内存的顶部向下增长,而堆从底部向上增长(这个决定取决于架构)。这允许它们单独增长,以便需要大量堆但不需要太多堆的程序可以使用与需要大量堆但不需要太多堆的程序相同的计划。