Malloc,Calloc用于测试我记忆的极限

时间:2012-08-23 09:25:32

标签: c calloc

我正在尝试编写一个c程序来测试我的系统上有多少内存。 我打算在各种不同的条件下运行它:

  1. 启用交换
  2. 禁用交换并将overcommit(/ proc / sys / vm / overcommit_memory)设置为false
  3. 禁用交换并将overcommit(/ proc / sys / vm / overcommit_memory)设置为true
  4. 在系统上运行的虚拟机内
  5. 我这样做是为了更多地了解内存分配在系统真实和虚拟内存的限制下的行为。

    我在4GB RAM和8GB Swap的机器上运行。

    我目前的情况是这样的:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        int *ptr;
        int mb = 1048576;
        long long int i;
    
        for (i = 0; i < 1000000000000; i++)
        {
            printf("Trying to allocate %lld MBytes\n", i * 10 * sizeof(int) );
    
            ptr = (int*) calloc(10 * mb, sizeof(int));
            if ( ptr == 0 ) {
                // clean up
                free(ptr);
                printf("Ran out of memory\n");
                return 1;
            }
        }
    }
    

    我希望这会继续分配40mb的块(sizeof(int)在我的系统上是4)。 Calloc会将内存初始化为零。当没有更多可用内存时,它将终止程序并释放内存。

    当我运行它时,它会继续超出我记忆的极限。它最终在打印线时死亡: “试图分配5707960 MBytes。” (表示近6000 GB的内存。)

    任何人都可以弄清楚我哪里出错了。

    感谢@Blank Xavier指出在分配这种方式时应该考虑页面文件大小。

    我修改了代码如下:

    int main(void)
    {
        int *ptr;
        int mb = 1048576;
        int pg_sz = 4096;
    
        long long int i;
    
        for (i = 0; i < 1000000000000; i++)
        {
            printf("Trying to allocate %lld MBytes\n", i * pg_sz * sizeof(int) / mb );
    
            ptr = (int*) calloc(pg_sz, sizeof(int));
            if ( ptr == 0 ) {
                // clean up
                free(ptr);
                printf("Ran out of memory\n");
                return 1;
            }
        }
    }
    

    现在它轰炸了印刷:

    “尝试分配11800 MBytes”

    这是我期望的4GB Ram和8GB交换。顺便说一下,由于它正在交换到磁盘,它在4GB之后打印得更慢。

4 个答案:

答案 0 :(得分:3)

首先,一些建议:

  

请不要从C中的分配函数中转换返回值 - 这可能会掩盖某些错误,这些错误几乎当然会在某些时候咬你(例如没有在系统中定义原型)指针和整数的宽度不同。)

     

分配函数返回void *,它完全能够隐式地强制转换为任何其他指针。

就你的实际问题而言,我将从前面的大量分配开始,确保它失败,至少在没有某些优化的系统上(a)

在这种情况下,你会逐渐退出,直到第一个成功。这样,您不必担心内务处理信息在内存领域占用太多空间或内存碎片的可能性:您只需找到立即成功的最大单个分配。

换句话说,就像下面的psuedo-code:

allocsz = 1024 * 1024 * 1024
ptr = allocate_and_zero (allocsz)
if ptr != NULL:
    print "Need to up initial allocsz value from " + allocsz + "."
    exit
while ptr == NULL:
    allocsz = allocsz - 1024
    ptr = allocate_and_zero (allocsz)
print "Managed to allocate " + allocsz + " bytes."

(a):至于为什么你的系统calloc似乎返回的内存多于你在swap中的内存,GNU libc将超过一定的阈值大小,使用{{ 1}}而不是堆,因此您的分配不限于交换文件大小(其后备存储在其他地方)。来自Linux下的mmap文档:

  

通常,malloc()从堆中分配内存,并根据需要使用sbrk(2)调整堆的大小。当分配大于MMAP_THRESHOLD字节的内存块时,glibc malloc()实现使用mmap(2)将内存分配为私有匿名映射。

这是一个我的解决方案无法解决的问题,因为它的初始大量分配也将高于阈值。

就物理内存而言,由于您已经在使用malloc,因此您应该只看一下procfs/proc/meminfo应该为您提供可用的物理内存(可能是完整的4G,因为某些地址空间被盗用于其他目的)。

对于虚拟内存分配,请将分配大小保持在阈值以下,以使它们从堆中而不是MemTotal区域中出来。 Linux mmap文档中的另一个片段:

  

MMAP_THRESHOLD默认为128 kB,但可以使用mallopt(3)进行调整。

因此,您的选择是使用malloc来增加阈值,或者稍微降低10M分配大小(比如说64K)。

答案 1 :(得分:3)

您无法通过大量分配来确定物理内存,然后在成功之前减少分配大小。

这只会确定最大可用虚拟内存块的大小。

您需要循环,分配物理页面大小(也将是虚拟页面大小)块,直到分配失败。使用这么简单的程序,您无需担心跟踪分配 - 操作系统将在程序退出时返回它们。

答案 2 :(得分:0)

嗯,程序排序符合你的意图,但是庞大的数字需要一个LL,或者它将被编译器“随机”截断,将数字视为一个整数。

但是,当你释放指针(它是一个空指针)时,它实际上不会做任何事情。它不会释放你分配的所有内存,因为你没有指向它。

此外,如果你退出主循环,那么从main返回的内容是未定义的。

答案 3 :(得分:-1)

如果循环成功,您必须释放已分配的内存

for (i = 0; i < 1000000000000; i++)
{
    printf("Trying to allocate %lld MBytes\n", i * 10 * sizeof(int) );

    ptr = (int*) calloc(10 * mb, sizeof(int));
    if ( ptr == 0 ) {
        printf("Ran out of memory\n");
        return 1;
    }
    else 
        free(ptr);
}