malloc可以分配的最大内存

Vik*_*kas 44 c malloc operating-system memory-management computer-architecture

我试图在我的机器(1 Gb RAM 160 Gb HD Windows平台)上计算出最大程度上可以使用多少内存.

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

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

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

int main(){  
    int *p;
    while(1){
        p=(int *)malloc(4);
        if(!p)break;
    }   
}
Run Code Online (Sandbox Code Playgroud)

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

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

一些问题:

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

Ale*_*lli 47

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

错误:大多数计算机/操作系统支持虚拟内存,由磁盘空间支持.

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

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

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

为什么没有分配失败?

你刚才要求的次数太少:最终循环会中断(在你的机器由于虚拟物理内存和物理内存的大量过剩而导致爬行速度变慢,以及随之而来的超级频繁磁盘访问,这个问题被称为"颠簸" ")但在此之前它已经耗尽了你的耐心.尝试一次获得例如一兆字节.

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

总停止不太可能,但是当通常需要几微秒的操作最终花费(例如)几十毫秒时,这四个数量级当然可以使感觉好像计算机基本停止了,通常需要什么一分钟可能需要一个星期.

  • 实际上在某些平台上,即使请求的大小超过RAM +交换大小,malloc也可能成功.在Linux上,例如,要求内存意味着映射`/ dev/zero`,这反过来意味着只是将页面标记为零 - 除非您更改内容,否则它不必消耗大量内存或交换空间. (3认同)
  • 你的意思是"捶打"吗? (2认同)

Seb*_*ian 25

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

#include <stdlib.h>

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

$ gcc memtest.c
$ ./a.out
Run Code Online (Sandbox Code Playgroud)

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

这段代码迅速填满了我的RAM(4Gb),然后在大约2分钟内我的20Gb交换分区才死亡.当然是64位Linux.

  • 我刚刚在具有192Gb内存/ 4Gb交换的机器上尝试了相同的程序.在一分钟内消耗高达175Gb,然后交换缓慢填充.当剩下只有24kb的掉期时,它就被杀死了. (2认同)
  • 一个好的“calloc(3)”实现在从“mmap(2)”获取页面后不会触及页面,因为它们已经归零。这实际上最终触发 OOM 杀手的原因是 malloc 的额外簿记信息使用内存。如果你“strace”它,你会看到“mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4fc4d14000”。分配大小 1052672 是 1MiB + 4096,而这个额外的页面可能是 glibc 的 malloc 实际上弄脏的。例如,在具有 5GiB 物理内存的桌面上,我可以在没有磁盘活动的情况下“calloc”16GiB(以 1MiB 块的形式)。 (2认同)

Cir*_*四事件 7

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

例如,在 Ubuntu 19.04 上,我们可以很容易地看到它mallocmmap(MAP_ANONYMOUS通过使用strace.

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

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

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

模式0下,不检查带有MAP_NORESERVE的mmap(2)调用,默认检查很弱,导致进程“OOM-killed”的风险。

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

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

CommitLimit = (total_RAM - total_huge_TLB) * overcommit_ratio / 100 + total_swap
Run Code Online (Sandbox Code Playgroud)

在哪里:

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

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

从 Linux 3.14 开始,如果 /proc/sys/vm/overcommit_kbytes 中的值非零,则 CommitLimit 计算如下:

CommitLimit = overcommit_kbytes + total_swap
Run Code Online (Sandbox Code Playgroud)

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

5.2.1 内核树中的Documentation/vm/overcommit-accounting.rst也提供了一些信息,虽然有点少:

Linux 内核支持以下过载处理模式

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

  • 1总是过度使用。适用于某些科学应用。经典示例是使用稀疏数组的代码,并且仅依赖几乎完全由零页组成的虚拟内存。

  • 2不要过度使用。系统的总地址空间提交不得超过交换区 + 可配置的物理 RAM 量(默认为 50%)。根据您使用的数量,在大多数情况下,这意味着进程在访问页面时不会被终止,但会在适当的内存分配中收到错误。

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

最小的实验

我们可以很容易地看到允许的最大值:

主文件

#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;
}
Run Code Online (Sandbox Code Playgroud)

GitHub 上游.

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

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out 0x40000000
./main.out 0x10000000000
Run Code Online (Sandbox Code Playgroud)

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

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

mmap: Cannot allocate memory
Run Code Online (Sandbox Code Playgroud)

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

echo 1 | sudo tee /proc/sys/vm/overcommit_memory
Run Code Online (Sandbox Code Playgroud)

那么 1TiB 分配工作正常。

模式2有据可查,但我懒得进行精确计算来验证它。但我只想指出,在实践中,我们可以分配以下内容:

overcommit_ratio / 100
Run Code Online (Sandbox Code Playgroud)

总RAM,并且overcommit_ratio50默认的,所以我们可以对分配内存总量的一半。

VSZ 与 RSS 以及内存不足的杀手

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

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

我已经详细说明了:什么是 Linux 内存管理中的 RSS 和 VSZ


mdm*_*dma 6

malloc它有自己的内存管理,自己管理小内存块,但最终它使用Win32 Heap函数来分配内存.您可以将其malloc视为"内存转销商".

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

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


hum*_*.js 6

试试这个

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

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

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

  • 在Linux上,使用默认的虚拟内存设置,您的程序最终将被杀死(使用SIGKILL),而不是malloc实际上返回NULL。 (2认同)
  • 就像其他人指出的那样,这不会按预期工作。(我想有人需要写 Deep C Secrets Secrets)。它将被杀死而不是返回空指针。根据您的系统,它也可能使用交换甚至磁盘支持的空间。如果您的系统使用内存过量使用以及对已分配内存的延迟评估,在触发操作系统逻辑之前,它很容易看起来支持数万*千兆字节*等,因为操作系统逻辑说由于内存不足而终止进程. (2认同)