了解内存分配,测试程序崩溃
我刚刚读完K&R,这就是我所知道的所有C语言。 我的所有编译都是使用MinGW从Windows命令行完成的,我不了解高级调试方法(因此我的第二个程序中的“ghetto debug”注释)。
我正在尝试制作一些小型测试程序,以帮助我更好地理解内存分配的工作原理。 这些前几个程序不使用malloc或free,我只是想看看如何为函数本地的标准数组分配和解除内存。 我的想法是,我观察我的运行进程RAM使用情况,看它是否符合我的理解。 对于下面的第一个程序,它确实按预期工作。 alloc_one_meg()
函数分配并初始化250,000个4字节整数,但是一旦函数返回,MB就会被alloc_one_meg()
分配。 因此,如果我连续10000次调用该函数,我永远不会看到我的RAM使用量远远高于1MB。 而且,它的工作原理。
#include #include void alloc_one_meg() { int megabyte[250000]; int i; for (i=0; i<250000; i++) { megabyte[i] = rand(); } } main() { int i; for (i=0; i<1000000; i++) { alloc_one_meg(); } }
对于下面的第二个程序,我们的想法是不允许函数退出,同时运行1000个相同函数的副本,这是我用递归完成的。 我的理论是,在递归完成之后,程序将消耗1GB的RAM,然后才将其全部解除分配。 但是,它不会通过递归超过第二个循环(请参阅我的ghetto调试注释)。 该程序崩溃了一个非常非信息(对我而言)的消息(Windows弹出窗口说____。exe遇到了问题)。 通常我总是可以通过我的贫民窟调试方法找到问题的根源……但它在这里不起作用。 我很难过。 这段代码有什么问题? 谢谢!
#include #include int j=0; void alloc_one_meg() { int megabyte[250000]; int i; for (i=0; i<250000; i++) { megabyte[i] = rand(); } j++; printf("Loop %d\n", j); // ghetto debug if (j<1000) { alloc_one_meg(); } } main() { alloc_one_meg(); }
后续问题发布在这里 。
你正在遇到堆栈溢出。
在堆栈上分配本地自动存储变量(例如megabyte
),其具有有限的空间量。 malloc在堆上分配,这允许更大的分配。
你可以在这里阅读更多:
http://en.wikipedia.org/wiki/Stack_overflow
(我应该注意,C语言没有指定内存的分配位置 – 堆栈和堆是实现细节)
Windows程序中堆栈的大小通常约为1 MB,因此在第二次递归时,堆栈溢出。 你不应该在堆栈上分配这么大的数组,使用malloc
并且可以free
地分配和释放堆上的内存(对于这样大小的数组,没有办法绕过malloc
):
void alloc_one_meg() { int *megabyte = malloc(sizeof(int) * 250000); // allocate space for 250000 // ints on the heap int i; for (i=0; i<250000; i++) { megabyte[i] = rand(); } j++; printf("Loop %d\n", j); // ghetto debug if (j<1000) { alloc_one_meg(); } free(megabyte); // DO NOT FORGET THIS }
也就是说,您实际上可以更改程序的堆栈大小并使其更大(尽管我只是将其作为教育练习,而不是生产代码)。 对于Visual Studio,您可以使用/ F编译器选项 ,在Linux上,您可以使用setrlimit(3)
。 我不知道MinGW应该使用什么。
通过递归函数调用分配的内存是从堆栈中分配的。 所有堆栈内存必须是连续的。 当您的进程启动一个线程时,Windows将为该线程的堆栈保留一系列虚拟内存地址空间。 要保留的内存量在EXE文件的“PE头”中指定。 PE代表“Portable Executable”。
使用Visual Studio附带的dumpbin
实用程序,将自身( dumpbin.exe
)作为输入文件:
dumpbin /headers dumpbin.exe
…有一些输出,然后:
100000 size of stack reserve 2000 size of stack commit
“100000”是hex数,等于1,048,576,因此这代表大约1MB。
换句话说,操作系统将仅为堆栈保留1MB的地址范围。 当该地址范围用完时,Windows可能会或可能无法分配更多连续的内存范围来增加堆栈。 结果取决于是否有更进一步的连续地址范围。 由于Windows在线程开始时进行了其他分配,因此它不太可能可用。
要在Windows下分配最大量的虚拟内存,请使用VirtualAlloc
系列函数。
堆栈溢出。 这是一个棘手的问题吗?