私は自分のマシンでどれだけのメモリを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
が割り当てることのできる最大メモリは、物理メモリ(ヒープ)に制限されていると読みました。
間違った例:ほとんどのコンピューター/ OSは仮想メモリをサポートしますが、ディスクスペースが必要です。
いくつかの質問:
malloc
はHDDからメモリも割り当てますか?
malloc
はOSに尋ねます。OSはディスクスペースを使用する可能性があります。
上記の動作の理由は何ですか?なぜループはいつでも中断しなかったのですか?
割り当てエラーがなかったのはなぜですか?
一度に要求するのは少なすぎます:ループは最終的に壊れます(仮想メモリと物理メモリの大過剰と、結果として生じる超頻繁なディスクアクセスのためにマシンがクロールに遅くなった後、「スラッシング」として知られる問題")しかし、それはその前にあなたの忍耐を使い果たしました。取得してみてください代わりに一度に1メガバイト。
プログラムがメモリの消費を一定のレベルまで超えると、他のアプリケーションが必要なメモリを十分に取得できないため、コンピューターの動作が停止します。
完全に停止することはほとんどありませんが、通常数マイクロ秒かかる操作に数十ミリ秒かかる場合、これらの4桁は確かにそれを行う可能性がありますfeel停止し、通常1分かかるものは1週間かかる可能性があります。
私はこのスレッドが古いことを知っていますが、試してみたい人はこのコードを使用してください
#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。
malloc
は独自のメモリ管理を行い、小さなメモリブロック自体を管理しますが、最終的にはWin32 ヒープ関数 を使用してメモリを割り当てます。 malloc
は「メモリ再販業者」と考えることができます。
Windowsメモリサブシステムは、物理メモリ(RAM)と仮想メモリ(HD)で構成されます。物理メモリが不足すると、ページの一部を物理メモリからハードドライブの仮想メモリにコピーできます。 Windowsはこれを透過的に行います。
デフォルトでは、仮想メモリは有効になっており、HDの使用可能なスペースを消費します。したがって、テストはプロセスに仮想メモリの全量を割り当てる(32ビットウィンドウで2GB)か、ハードディスクがいっぱいになるまで実行を続けます。
これを試して
#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の秘密。
実際にそれが失敗した理由はわかりませんが、注意すべきことの1つは、「malloc(4)」が実際に4バイトを与えない場合があることです。したがって、この手法は実際に最大ヒープサイズを見つける正確な方法ではありません。
私の質問からこれを見つけました here 。
たとえば、4バイトのメモリを宣言すると、カーネルが要求したメモリ量を示すために、メモリの直前のスペースに整数4を含めることができます。
C90規格では、サイズが32 KBのオブジェクトを少なくとも1つ取得できることが保証されており、これは静的、動的、または自動メモリの場合があります。 C99は少なくとも64 kByteを保証します。上限については、コンパイラのドキュメントを参照してください。
また、mallocの引数はsize_tであり、そのタイプの範囲は[0、SIZE_MAX]であるため、可能な最大値はrequest SIZE_MAXです。この値は実装によって異なり、<limits.h>
で定義されます。
/proc/sys/vm/overcommit_memory
はLinuxの最大値を制御します
たとえば、Ubuntu 19.04では、 malloc
を使用すると、strace
がmmap(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;
}
コンパイルして実行し、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とは