4

为了测试内存不足的行为,我在 32 位 Linux 3.2 上使用 GCC 4.7.1 编译了以下 C 程序,没有任何编译器标志:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
    while (malloc(4096)) ;
    printf("%s", strerror(errno));
    return 0;
}

当我运行程序时,我观察到 malloc 在分配了大约 2.5 GB 的常驻内存后失败(“无法分配内存”)。

这台机器有 2GB 的物理内存和 4GB 的交换空间。在程序运行期间未观察到内核消息。

那么为什么 Linux 停止提供内存呢?

相关问题:malloc 可以分配的最大内存,但它没有解决 Linux 的具体问题。

4

4 回答 4

9

机器中的物理内存量与malloc. 一个进程有一个固定的虚拟地址空间(对于 32 位进程,通常是 2GB)。的实现malloc将返回地址,直到用完虚拟地址空间而不是物理 RAM。

这是一个更详细的讨论

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

于 2013-07-29T23:30:00.717 回答
4

您正在观察的是虚拟机溢出。

32 位进程只能访问 4GB 的虚拟地址空间。该虚拟地址空间必须容纳

  • 您的过程文本
  • 您加载的任何共享库的文本(libc、libdl、...)
  • 至少一叠
  • vsyscalls 的文本
  • 任何其他 VM 映射

操作系统通常会对 CPU 上的内存控制器进行编程,以便为内核页面(其中的 vsyscalls)保留(大部分)VM 空间,并将进程可用的内存限制在 4GB 以下。这曾经是虚拟机的一半,现在更少了。

glibc的实现malloc为您的堆保留一个(或多个)VM空间块,但它被编程为为共享映射留下足够的VM空间。因此,根据虚拟地址空间中的其他内容,您可能会在整个 VM 饱和之前遇到“内存不足”。

请咨询/proc/[pid]/maps您的 VM 映射的位置。

使用完整 VM 的唯一方法是使用mmap和系列调用在空闲 VM 中的任何位置分配内存。尝试:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>

int main() {
    char * page;
    size_t memory_allocated = 0;
    while ((page = mmap(NULL, sysconf(_SC_PAGE_SIZE)
                        , PROT_READ | PROT_WRITE
                        , MAP_ANON | MAP_PRIVATE, -1, 0))
                 != MAP_FAILED) {
        memory_allocated += sysconf(_SC_PAGE_SIZE);
        // optionally touch the page
        // otherwise the mapping won't use physical RAM/swap
        // *page = 1; 
    }

    fprintf(stderr, "Allocated %zu MiB\n", memory_allocated >> 20);
    perror("mmap");
    return 0;
}

请不要将此作为 64 位二进制文​​件尝试,因为它会尝试在那里使用高达 256TB 的 RAM。

在我的 Mac OS X 上,我得到:

$ gcc -m32 17935873.c
$ ./a.out
Allocated 3516 MiB
mmap: Cannot allocate memory
于 2013-08-01T16:49:47.130 回答
0

root您是否以特权运行此代码?一旦我也尝试了具有user特权的内存分配代码,我得到了错误。

这不是你的答案,但我想在这里分享我的示例代码,也许我们可以从中学到一些东西。使用这种方法,我们可以分配所需的内存量,直到内存用完。这是有效的代码(如果您尝试获取更多内存,则会出错)。

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define A_MEGABYTE (1024 * 1024)
#define PHY_MEM_MEGS 1024 /* Adjust this number as required */
int main()
{
    char *some_memory;
    size_t size_to_allocate = A_MEGABYTE;
    int megs_obtained = 0;
    while (megs_obtained < (PHY_MEM_MEGS * 2))
    {
        some_memory = (char *)malloc(size_to_allocate);
        if (some_memory != NULL)
        {
            megs_obtained++;
            sprintf(some_memory, “Hello World”); //Message to check
            printf(“%s - now allocated %d Megabytes\n”, some_memory, megs_obtained);
        }
        else
        {
             exit(EXIT_FAILURE);
        }
    }
    exit(EXIT_SUCCESS);
}

可能这会有所帮助。

EDIT

这是我的理解,如果我错了,请告诉我。

正如您所说,您正在获取2.5 GB内存,这是因为堆可以分配2/3 times其内存。所以你会Max 3 GB4 GB记忆中得到。Rest memory occupied by some processes and definitely process scheduler. 没有智能操作系统允许自己自杀。

在这里我读到Operating System了书Galvin。根据您的情况的书,如果您想分配更多内存,只需调用malloc1000 次(每个 malloc 调用单行),或者直到Page Size操作系统。除非您交换内存中的页面,否则您将不会得到Kernel Message(理论上)。您的页面没有从内存中换出,这是唯一的问题。

如果您想检查所有Swap area编写此代码的代码,Thread那么sleep您可以检查交换内存中的所有进程。

我没有为此编写任何代码,因此如果您编写了一些代码,请更新我们。

于 2013-08-01T05:14:50.133 回答
0

内存可能是碎片化的,因此有几个小于 4k 的空间块。malloc 只有在找到请求的空间时才会返回指向已分配内存的指针。

此外,在未分配的存储中嵌入了指向或链接空闲区域的指针(每个 K&R)。这是适用于 Linux 和 Windows 的一般性陈述。

在 Windows 中,我转储了 malloc 返回的指针周围的区域,发现 Windows 有大约 16 个字节的信息(我认为主要是句柄)作为每个 malloc 区域的前缀。这些前缀不会返回给 malloc 的调用者,但它们会占用空间并减少特定机器上的可用区域。

您可以尝试使用第二个循环来降低分配:

   int main()
   {
       int avail = 4096;

       while (malloc(avail)) ;
       printf("%s", strerror(errno));

       avail = avail >> 1;
       while (malloc(avail)) ;
       printf("%s", strerror(errno));

       // etc. down to whatever granularity you want
       // or even better code an inner lup to do this

       return 0;
   }
于 2013-08-01T07:13:54.670 回答