了解内存分配,测试程序崩溃

我刚刚读完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系列函数。

堆栈溢出。 这是一个棘手的问题吗?