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

时间:2011-12-23 00:25:20

标签: c

我刚刚读完K& R,这就是我所知道的所有C语言。我的所有编译都是使用MinGW从Windows命令行完成的,我不了解高级调试方法(因此我的第二个程序中的“ghetto debug”注释)。

我正在尝试制作一些小型测试程序,以帮助我更好地理解内存分配的工作原理。这些前几个程序不使用malloc或free,我只是想看看如何为函数本地的标准数组分配和解除分配内存。我的想法是,我观察我的运行进程RAM使用情况,看它是否符合我的理解。对于下面的第一个程序,它确实按预期工作。 alloc_one_meg()函数分配并初始化250,000个4字节整数,但只要函数返回就会取消分配MB。因此,如果我连续10000次调用该函数,我永远不会看到我的RAM使用量远远高于1MB。而且,它有效。

#include <stdio.h>
#include <stdlib.h>

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 <stdio.h>
#include <stdlib.h>

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();
}

发布了here后续问题。

4 个答案:

答案 0 :(得分:3)

你正在遇到堆栈溢出。

本地自动存储变量(例如megabyte)在堆栈上分配,其空间有限。 malloc在堆上分配,这允许更大的分配。

您可以在这里阅读更多内容:

http://en.wikipedia.org/wiki/Stack_overflow

(我应该注意,C语言没有指定内存的分配位置 - 堆栈和堆是实现细节)

答案 1 :(得分:2)

Windows程序中堆栈的大小通常约为1 MB,因此在第二次递归时,堆栈溢出。你不应该在堆栈上分配这么大的数组,使用mallocfree来分配和释放堆上的内存(对于这样的大小,你无法绕过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,您可以use the /F compiler option,在Linux上,您可以使用setrlimit(3)。我不知道MinGW应该使用什么。

答案 2 :(得分:1)

通过递归函数调用分配的内存是从堆栈中分配的。所有堆栈内存必须是连续的。当您的进程启动一个线程时,Windows将为该线程的堆栈保留一系列虚拟内存地址空间。要保留的内存量在EXE文件&#34; PE头中指定。&#34; PE代表&#34; Portable Executable。&#34;

使用Visual Studio附带的dumpbin实用程序,将其自身(dumpbin.exe)作为输入文件:

dumpbin /headers dumpbin.exe

...有一些输出,然后:

      100000 size of stack reserve
        2000 size of stack commit

&#34; 100000&#34;是十六进制数字,等于1,048,576,所以这代表大约1MB。

换句话说,操作系统只会为堆栈保留1MB的地址范围。当该地址范围用完时,Windows可能会或可能无法分配更多连续的内存范围来增加堆栈。结果取决于是否有更进一步的连续地址范围。由于Windows在线程开始时进行了其他分配,因此它不太可能可用。

要在Windows下分配最大数量的虚拟内存,请使用VirtualAlloc系列函数。

答案 3 :(得分:0)

StackOverflow的。这是一个棘手的问题吗?