57

我试图弄清楚我可以在我的机器上最大程度地分配多少内存(1 Gb RAM 160 Gb HD Windows 平台)。

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

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

所以为了确认,我用 C 写了一个小程序:

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

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

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

一些问题:

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

8 回答 8

54

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

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

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

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

上述行为的原因是什么?为什么循环没有随时中断?

为什么没有分配失败?

您一次只要求太少:循环最终会中断(在您的机器由于虚拟内存与物理内存的大量过剩以及随之而来的超频繁磁盘访问而减速到爬行之后,这个问题被称为“抖动") 但在那之前它已经耗尽了你的耐心。尝试一次获取例如兆字节。

当一个程序的内存消耗超过一定水平时,计算机就会停止工作,因为其他应用程序没有获得足够的内存。

完全停止是不可能的,但是当一个通常需要几微秒的操作最终需要(例如)几十毫秒时,这四个数量级肯定会让人感觉好像计算机已经基本停止了,而通常需要什么一分钟可能需要一周。

于 2010-05-09T16:38:18.627 回答
26

我知道这个线程很旧,但是对于任何愿意自己尝试的人,使用这段代码

#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

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

于 2011-01-19T13:30:26.723 回答
10

/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

在哪里:

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

例如,在具有 16GB 物理 RAM、16GB 交换空间、没有专用于大页面的空间以及 overcommit_ratio 为 50 的系统上,此公式得出的 CommitLimit 为 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内核支持以下overcommit处理模式

  • 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;
}

GitHub 上游.

编译运行分配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

总内存,默认overcommit_ratio情况50下,所以我们可以分配大约一半的总内存。

VSZ vs RSS 和内存不足的杀手

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

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

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

于 2019-08-28T07:51:15.083 回答
8

尝试这个

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

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

包括 stdlib 和 stdio 。
此摘录取自deep c secrets

于 2012-09-09T06:18:51.253 回答
7

malloc做自己的内存管理,自己管理小内存块,但最终它使用 Win32 Heap 函数来分配内存。您可以将其malloc视为“内存经销商”。

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

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

于 2010-05-09T16:40:14.237 回答
4

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

此外,malloc 的参数是 size_t 并且该类型的范围是 [0,SIZE_MAX],因此您可以请求的最大值是 SIZE_MAX,该值因实现而异,并在<limits.h>.

于 2012-02-01T10:18:31.367 回答
3

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

我从这里的问题中发现了这一点。

例如,当您声明 4 个字节的内存时,内存前面的空间可以包含整数 4,作为向内核指示您请求多少内存的指示。

于 2010-05-09T16:38:11.747 回答
-3

当您第一次为 *p 分配任何大小时,下次您将该内存设为未引用时。这意味着

一次您的程序仅分配 4 个字节的内存

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

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

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

于 2014-07-07T13:36:39.073 回答