2

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

  1. 启用交换
  2. 禁用交换并且过度使用 (/proc/sys/vm/overcommit_memory) 设置为 false
  3. 禁用交换并且过度使用 (/proc/sys/vm/overcommit_memory) 设置为 true
  4. 在系统上运行的虚拟机内部

我这样做是为了更多地了解内存分配在系统真实和虚拟内存的限制下如何表现。

我在具有 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 MB”

这就是我对 4GB Ram 和 8GB 交换的期望。顺便说一句,它在 4GB 之后打印速度要慢得多,因为它正在交换到磁盘。

4

4 回答 4

3

首先,一些建议:

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

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

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

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

换句话说,类似于以下伪代码:

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在您的系统上返回的内存似乎比交换中的更多,GNU libc 将在某个阈值大小之上使用mmap而不是堆,因此您的分配不受交换文件大小的限制(它的后备存储在其他地方)。从mallocLinux下的文档:

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

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

在物理内存方面,既然您已经在使用procfs,您可能应该只看一下内部/proc/meminfoMemTotal应该为您提供可用的物理内存(可能不是完整的 4G,因为其中一些地址空间被盗用于其他目的)。

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

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

所以你的选择是用来mallopt增加阈值,或者稍微降低 10M 的分配大小(比如 64K)。

于 2012-08-23T09:29:53.740 回答
3

您不能通过巨大的分配来确定物理内存,然后减少分配大小直到成功。

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

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

于 2012-08-23T09:53:29.893 回答
0

好吧,程序按照您的意图进行,但是巨大的数字后面需要一个 LL,否则它会被编译器“随机”截断,编译器会将数字视为整数。

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

此外,如果您退出主循环,则从 main 中返回的内容是未定义的。

于 2012-08-23T09:38:31.363 回答
-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);
}
于 2012-08-23T09:35:34.530 回答