malloc可以分配的最大内存

时间:2010-05-09 16:31:47

标签: c memory-management operating-system malloc computer-architecture

我试图弄清楚我可以在我的机器上最大程度地使用malloc的内存量 (1 Gb RAM 160 Gb HD Windows平台)。

我读到malloc可以分配的最大内存仅限于物理内存(堆上)。

此外,当程序超出内存消耗到某个级别时,计算机将停止工作,因为其他应用程序无法获得足够的内存。

所以要确认一下,我在C中写了一个小程序:

int main(){  
    int *p;
    while(1){
        p=(int *)malloc(4);
        if(!p)break;
    }   
}

我希望有一段时间内存分配会失败并且循环会中断,但我的计算机会挂起,因为它是一个无限循环。

我等了大约一个小时,最后我不得不强行关闭电脑。

有些问题:

  • malloc是否也从HD分配内存?
  • 上述行为的原因是什么?
  • 为什么在任何时候都没有循环中断?
  • 为什么没有分配失败?

8 个答案:

答案 0 :(得分:46)

  

我读到最大内存malloc可以分配的内存仅限于物理内存(堆上)。

错误:大多数计算机/操作系统都支持虚拟内存,并以磁盘空间为后盾。

  

一些问题:malloc是否也从HDD分配内存?

malloc询问操作系统,反过来可能会使用一些磁盘空间。

  

上述行为的原因是什么?为什么循环在任何时候都没有中断?

     

为什么没有分配失败?

你刚才要求的次数太少:最终循环会中断(在你的机器由于虚拟物理内存和物理内存大量过剩以及随之而来的超常频繁磁盘访问而慢慢爬行之后作为“捶打”但是在此之前它已经耗尽了你的耐心。尝试获取例如一次一兆字节。

当程序超出内存消耗到某个级别时,计算机将停止工作,因为其他应用程序无法获得足够的内存。

完全停止是不太可能的,但是当通常需要几微秒的操作最终花费(例如)数十毫秒时,这四个数量级当然可以使它感觉感觉计算机基本停止了,通常需要一分钟的时间可能需要一周时间。

答案 1 :(得分:24)

我知道这个帖子已经过时了,但对于任何愿意自己试一试的人来说,请使用此代码剪掉

#include <stdlib.h>

int main() {
int *p;
while(1) {
    int inc=1024*1024*sizeof(char);
    p=(int*) calloc(1,inc);
    if(!p) break;
    }
}

运行

$ gcc memtest.c
$ ./a.out

运行时,此代码填充RAM,直到被内核杀死。使用calloc而不是malloc来防止“懒惰评估”。从这个线程中获取的想法: Malloc Memory Questions

这段代码很快就填满了我的内存(4Gb),然后在大约2分钟后我的20Gb交换分区就死了。当然是64位Linux。

答案 2 :(得分:6)

malloc执行自己的内存管理,自己管理小内存块,但最终它使用Win32 Heap functions来分配内存。您可以将malloc视为“内存转销商”。

Windows内存子系统包括物理内存(RAM)和虚拟内存(HD)。当物理内存变得稀缺时,某些页面可以从物理内存复制到硬盘驱动器上的虚拟内存。 Windows透明地执行此操作。

默认情况下,启用虚拟内存并消耗HD上的可用空间。因此,您的测试将继续运行,直到它为该进程分配了全部虚拟内存(32位窗口上为2GB)或填满了硬盘。

答案 3 :(得分:5)

试试这个

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

main() {
    int Mb = 0;
    while (malloc(1<<20)) ++Mb;
    printf("Allocated %d Mb total\n", Mb);
}

包括stdlib和stdio。
该提取物取自深层秘密

答案 4 :(得分:3)

我实际上并不知道为什么会失败,但需要注意的一点是`malloc(4)“实际上可能不会给你4个字节,所以这种技术并不是找到最大堆大小的准确方法。

我在问题here中找到了这个。

例如,当你声明4个字节的内存时,直接在你的内存之前的空间可能包含整数4,作为内核的一个指示你要求的内存。

答案 5 :(得分:2)

根据C90标准,您可以保证至少可以获得一个32 KB大小的对象,这可能是静态,动态或自动内存。 C99保证至少64 kBytes。有关任何更高的限制,请参阅编译器的文档。

此外,malloc的参数是size_t,该类型的范围是[0,SIZE_MAX],因此您可以请求的最大值是SIZE_MAX,该值在实现时会有所不同,并且在<limits.h>中定义。

答案 6 :(得分:1)

/proc/sys/vm/overcommit_memory控制Linux上的最大值

例如在Ubuntu 19.04上,we can easily see that malloc is implemented with mmap(MAP_ANONYMOUS by using strace

然后man proc然后说明/proc/sys/vm/overcommit_memory如何控制最大分配:

  

此文件包含内核虚拟内存记帐模式。值是:

     
      
  • 0:启发式过量使用(这是默认设置)
  •   
  • 1:总是过量使用,从不检查
  •   
  • 2:始终检查,永不过度使用
  •   
     

在模式0下,不检查带有MAP_NORESERVE的mmap(2)调用,并且默认检查非常弱,从而导致进程被“ OOM杀死”。

     

在模式1中,内核假装总是有足够的内存,直到内存实际用完为止。这种模式的一个用例是采用大型稀疏阵列的科学计算应用程序。在2.6.0之前的Linux内核版本中,任何非零值都表示模式1。

     

在模式2(从Linux 2.6开始可用)中,可以分配的总虚拟地址空间(/ proc / meminfo中的ComitLimit)计算为

CommitLimit = (total_RAM - total_huge_TLB) * overcommit_ratio / 100 + total_swap
     

其中:

     
      
  • total_RAM是系统上的RAM总量;
  •   
  • total_huge_TLB是为大页面预留的内存量;
  •   
  • overcommit_ratio是/ proc / sys / vm / overcommit_ratio中的值;和
  •   
  • total_swap是交换空间的数量。
  •   
     

例如,在具有16GB物理RAM,16GB交换空间,没有专用于大页面的空间以及50的overcommit_ratio的系统上,此公式得出的ComLimitLimit为24GB。

     

从Linux 3.14开始,如果/ proc / sys / vm / overcommit_kbytes中的值不为零,则CommitLimit的计算方式为:

CommitLimit = overcommit_kbytes + total_swap
     

另请参阅/ proc / sys / vm / admiin_reserve_kbytes和/ proc / sys / vm / user_reserve_kbytes的说明。

5.2.1内核树中的

Documentation/vm/overcommit-accounting.rst也提供了一些信息,尽管笑的少一些:

  

Linux内核支持以下过量使用处理模式

     
      
  • 0启发式过量使用处理。地址明显过量使用   空间被拒绝。用于典型的系统。它确保了   严重的野生分配失败,同时允许过量使用   减少交换使用。允许根分配更多   在此模式下的内存。这是默认设置。

  •   
  • 1始终过量使用。适合一些科学   应用程序。经典示例是使用稀疏数组和   仅仅依靠几乎全部组成的虚拟内存   零页。

  •   
  • 2不要过量使用。总的地址空间提交   系统不允许超过交换+可配置的数量   (默认值为50%)的物理RAM。根据您的数量   使用,在大多数情况下,这意味着一个过程将不会   在访问页面时被杀死,但是会收到内存错误   分配。

         

    对于希望保证其内存的应用程序很有用   分配将在将来可用,而无需   初始化每个页面。

  •   

最小实验

我们可以轻松地看到最大允许值:

main.c

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char *chars;
    size_t nbytes;

    /* Decide how many ints to allocate. */
    if (argc < 2) {
        nbytes = 2;
    } else {
        nbytes = strtoull(argv[1], NULL, 0);
    }

    /* Allocate the bytes. */
    chars = mmap(
        NULL,
        nbytes,
        PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS,
        -1,
        0
    );

    /* This can happen for example if we ask for too much memory. */
    if (chars == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    /* Free the allocated memory. */
    munmap(chars, nbytes);

    return EXIT_SUCCESS;
}

GitHub upstream

编译并运行以分配1GiB和1TiB:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out 0x40000000
./main.out 0x10000000000

然后我们可以使用分配值来查看系统允许的内容。

我找不到0(默认)的精确文档,但是在我的32GiB RAM计算机上,它不允许1TiB分配:

mmap: Cannot allocate memory

但是,如果我启用了无限过量使用:

echo 1 | sudo tee /proc/sys/vm/overcommit_memory

然后1TiB分配工作正常。

模式2有充分的文献资料,但是我懒于进行精确的计算来验证它。但我只想指出,实际上我们可以分配:

overcommit_ratio / 100

总RAM,而overcommit_ratio默认为50,因此我们可以分配总RAM的一半。

VSZ vs RSS和内存不足杀手

到目前为止,我们刚刚分配了虚拟内存。

但是,在某些时候,当然,如果您使用了足够多的页面,Linux将不得不开始杀死某些进程。

我已在What is RSS and VSZ in Linux memory management

处进行了详细说明。

答案 7 :(得分:-1)

第一次将任何大小分配给* p时,每次下次将该内存保留为未引用时。这意味着

  

您的程序只分配4个字节的内存

。那么你怎么能使用整个RAM,这就是为什么 SWAP设备(HDD上的临时空间)没有讨论的原因。我知道一种内存管理算法,当没有一个程序引用内存块时,该块有资格为程序内存请求分配。这就是为什么你只是忙于 RAM驱动程序这就是为什么它无法提供服务其他程序的机会。这也是一个悬空参考问题。

Ans:您最多可以分配RAM大小的内存。因为没有程序可以访问交换设备。

我希望你的所有问题都得到满意的答案。