web-dev-qa-db-ja.com

mallocが割り当て可能な最大メモリ

私は自分のマシンでどれだけのメモリをmallocできるかを把握しようとしていました(1 Gb RAM 160 Gb HD Windowsプラットフォーム)。

Mallocが割り当てることのできる最大メモリは、物理メモリ(ヒープ)に制限されていると読みました。

また、プログラムが特定のレベルまでメモリの消費を超えた場合、他のアプリケーションが必要なメモリを十分に取得できないため、コンピュータは動作を停止します。

確認のため、Cで小さなプログラムを作成しました。

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

メモリ割り当てが失敗し、ループが壊れる時間があることを期待していましたが、無限ループであるため、コンピューターがハングしました。

私は約1時間待機し、最後にコンピューターを強制的にシャットダウンする必要がありました。

いくつかの質問:

  • MallocはHDからもメモリを割り当てますか?
  • 上記の動作の理由は何ですか?
  • どの時点でもループブレークが発生しなかったのはなぜですか?
  • 割り当ての失敗がなかったのはなぜですか?
41
Vikas

mallocが割り当てることのできる最大メモリは、物理メモリ(ヒープ)に制限されていると読みました。

間違った例:ほとんどのコンピューター/ OSは仮想メモリをサポートしますが、ディスクスペースが必要です。

いくつかの質問:mallocはHDDからメモリも割り当てますか?

mallocはOSに尋ねます。OSはディスクスペースを使用する可能性があります。

上記の動作の理由は何ですか?なぜループはいつでも中断しなかったのですか?

割り当てエラーがなかったのはなぜですか?

一度に要求するのは少なすぎます:ループは最終的に壊れます(仮想メモリと物理メモリの大過剰と、結果として生じる超頻繁なディスクアクセスのためにマシンがクロールに遅くなった後、「スラッシング」として知られる問題")しかし、それはその前にあなたの忍耐を使い果たしました。取得してみてください代わりに一度に1メガバイト。

プログラムがメモリの消費を一定のレベルまで超えると、他のアプリケーションが必要なメモリを十分に取得できないため、コンピューターの動作が停止します。

完全に停止することはほとんどありませんが、通常数マイクロ秒かかる操作に数十ミリ秒かかる場合、これらの4桁は確かにそれを行う可能性がありますfeel停止し、通常1分かかるものは1週間かかる可能性があります。

47
Alex Martelli

私はこのスレッドが古いことを知っていますが、試してみたい人はこのコードを使用してください

#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

実行すると、このコードは、カーネルによって殺されるまで1 RAMを埋めます。「遅延評価」を防ぐためにmallocの代わりにcallocを使用します。このスレッドからのアイデア: Malloc Memory Questions

このコードはすぐに私のRAM(4Gb)を埋め、その後約2分で20Gbスワップパーティションが死ぬ前に埋められました。もちろん64ビットLinux。

25
Sebastian

mallocは独自のメモリ管理を行い、小さなメモリブロック自体を管理しますが、最終的にはWin32 ヒープ関数 を使用してメモリを割り当てます。 mallocは「メモリ再販業者」と考えることができます。

Windowsメモリサブシステムは、物理メモリ(RAM)と仮想メモリ(HD)で構成されます。物理メモリが不足すると、ページの一部を物理メモリからハードドライブの仮想メモリにコピーできます。 Windowsはこれを透過的に行います。

デフォルトでは、仮想メモリは有効になっており、HDの使用可能なスペースを消費します。したがって、テストはプロセスに仮想メモリの全量を割り当てる(32ビットウィンドウで2GB)か、ハードディスクがいっぱいになるまで実行を続けます。

6
mdma

これを試して

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

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

Stdlibとstdioを含めます。
この抽出物は 深いcの秘密

5
human.js

実際にそれが失敗した理由はわかりませんが、注意すべきことの1つは、「malloc(4)」が実際に4バイトを与えない場合があることです。したがって、この手法は実際に最大ヒープサイズを見つける正確な方法ではありません。

私の質問からこれを見つけました here

たとえば、4バイトのメモリを宣言すると、カーネルが要求したメモリ量を示すために、メモリの直前のスペースに整数4を含めることができます。

3
Chris Cooper

C90規格では、サイズが32 KBのオブジェクトを少なくとも1つ取得できることが保証されており、これは静的、動的、または自動メモリの場合があります。 C99は少なくとも64 kByteを保証します。上限については、コンパイラのドキュメントを参照してください。

また、mallocの引数はsize_tであり、そのタイプの範囲は[0、SIZE_MAX]であるため、可能な最大値はrequest SIZE_MAXです。この値は実装によって異なり、<limits.h>で定義されます。

2
mav_2k

/proc/sys/vm/overcommit_memoryはLinuxの最大値を制御します

たとえば、Ubuntu 19.04では、 mallocを使用すると、stracemmap(MAP_ANONYMOUSで実装されていることが簡単にわかります

次に、man proc/proc/sys/vm/overcommit_memoryが最大割り当てを制御する方法を説明します。

このファイルには、カーネル仮想メモリアカウンティングモードが含まれています。値は次のとおりです。

  • 0:ヒューリスティックなオーバーコミット(これがデフォルトです)
  • 1:常にオーバーコミット、チェックしない
  • 2:常に確認し、オーバーコミットしない

モード0では、MAP_NORESERVEを指定したmmap(2)の呼び出しはチェックされず、デフォルトのチェックは非常に弱く、プロセスが「OOMで強制終了」される危険性があります。

モード1では、カーネルはメモリが実際になくなるまで常に十分なメモリがあるように見せかけます。このモードの使用例の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のスワップ、巨大なページ専用のスペースがなく、50のovercommit_ratioがあるシステムでは、この式は24GBのCommitLimitをもたらします。

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の説明も参照してください。

Documentation/vm/overcommit-accounting.rst 5.2.1カーネルツリーの情報もいくつかの情報を提供しますが、少しは少ないですが:

Linuxカーネルは、次のオーバーコミット処理モードをサポートしています

  • 0ヒューリスティックなオーバーコミット処理。アドレス空間の明らかな超過は拒否されます。典型的なシステムに使用されます。オーバーコミットを許可してスワップの使用量を削減しながら、深刻なワイルド割り当てが失敗することを保証します。このモードでは、rootはわずかに多くのメモリを割り当てることができます。これがデフォルトです。

  • 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アップストリーム

コンパイルして実行し、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はいくつかのプロセスの強制終了を開始する必要があります。

詳細は次のとおりです。 Linuxメモリ管理におけるRSSおよびVSZとは