私はいくつかのコードを書いていて、それはクラッシュし続けました。後でダンプを調べた後、最大ヒープ制限をオーバーシュートしていることに気付きました(mallocのチェックを追加した方が楽だったでしょう)。修正しましたが、ヒープサイズを増やす方法はありますか?
PS:かなり 同様の質問 ここにありますが、返答は私には不明確です。
ヒープは通常、アーキテクチャ上のアドレス指定可能な仮想メモリと同じ大きさです。
_ulimit -a
_コマンドを使用してシステムの現在の制限を確認し、この行を最大memory size (kbytes, -m) 3008828
にしてください。この行は、OpenSuse 11.4 x86_64で〜3.5 GiB of ramプロセスごとに約3GBのRAMがあると言います。
次に、この簡単なプログラムを使用してシステムを実際にテストし、プロセスごとの最大使用可能メモリを確認できます。
_#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[]){
size_t oneHundredMiB=100*1048576;
size_t maxMemMiB=0;
void *memPointer = NULL;
do{
if(memPointer != NULL){
printf("Max Tested Memory = %zi\n",maxMemMiB);
memset(memPointer,0,maxMemMiB);
free(memPointer);
}
maxMemMiB+=oneHundredMiB;
memPointer=malloc(maxMemMiB);
}while(memPointer != NULL);
printf("Max Usable Memory aprox = %zi\n",maxMemMiB-oneHundredMiB);
return 0;
}
_
このプログラムは、100MiBの増分でメモリを取得し、現在割り当てられているメモリを提示し、それに0を割り当て、メモリを解放します。システムがメモリを追加できない場合、NULLを返し、RAMの最終的な最大使用可能量を表示します。
警告は、システムが最終段階でメモリを大量にスワップし始めることです。システム構成によっては、カーネルが一部のプロセスを強制終了する場合があります。私は100 MiBの増分を使用しているため、一部のアプリとシステム用に呼吸スペースがあります。クラッシュさせたくないものはすべて閉じてください。
言われていること。これを書いている私のシステムでは、何もクラッシュしませんでした。上記のプログラムは、_ulimit -a
_とほとんど同じではありません。違いは、実際にメモリをテストし、memset()
を使用してメモリが与えられ、使用されていることを確認したことです。
Ubuntu 10.04x86での比較のためにVM 256 MBのRAMと400MiBのスワップで、ulimitレポートはmemory size (kbytes, -m) unlimited
であり、私の小さなプログラムは524.288.000バイトを報告しました。他のソフトウェアとカーネルが使用するRAMとスワップを組み合わせた割引RAM。
編集:Adam Zalcmanが書いたように、_ulimit -m
_は、新しい2.6以降のLinuxカーネルでは使用できなくなったため、修正しました。しかし、_ulimit -v
_は尊重されます。実用的な結果を得るには、-mを-vに置き換え、virtual memory (kbytes, -v) 4515440
を探します。私のsuseボックスに-m値が私の小さなユーティリティが報告したものと一致することは単なるチャンスのようです。これはカーネルによって割り当てられた仮想メモリであることを覚えておいてください。物理RAMが不十分な場合、それを補うためにスワップ領域が必要になります。
プロセスやシステムに影響を与えずに使用可能な物理RAMの量を知りたい場合は、
long total_available_ram =sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) ;
これにより、キャッシュメモリとバッファメモリが除外されるため、この数は実際に使用可能なメモリよりもはるかに少なくなる可能性があります。 OSキャッシュは非常に大きくなる可能性があり、それらのエビクションは必要な追加メモリを提供できますが、それはカーネルによって処理されます。
ヒープおよびメモリ管理は、Cライブラリ(glibcなど)によって提供される機能です。 malloc()
を実行するたびに、ヒープを維持し、メモリのチャンクを返します。ヒープサイズの制限を認識していません。ヒープで利用可能なメモリよりも多くのメモリを要求するたびに、カーネルは(sbrk()
またはmmap()
を使用して)追加のメモリを要求します)。
デフォルトでは、カーネルはほとんどの場合、要求されたときにより多くのメモリを提供します。つまり、malloc()
は常に有効なアドレスを返します。カーネルがページを見つけるのに実際に煩わされるのは、割り当てられたページを初めて参照するときだけです。それを手に入れることができないことが判明した場合、それはOOMキラーを実行します。これは、badnessと呼ばれる特定の方法に従っています(これには、プロセスとその子の仮想メモリサイズが含まれます) 、ニースレベル、全体の実行時間など)被害者を選択し、SIGTERM
を送信します。このメモリ管理手法はオーバーコミットと呼ばれ、_/proc/sys/vm/overcommit_memory
_が0または1のときにカーネルによって使用されます。詳細については、カーネルのドキュメントの overcommit-accounting を参照してください。
_/proc/sys/vm/overcommit_memory
_に2を書き込むと、オーバーコミットを無効にできます。そうすると、カーネルは約束する前に実際にメモリがあるかどうかをチェックします。これにより、使用可能なメモリがなくなった場合、malloc()
はNULLを返します。
また、setrlimit()
および_RLIMIT_AS
_、または_ulimit -v
_コマンドを使用して、プロセスが割り当てることができる仮想メモリに制限を設定することもできます。上記のオーバーコミット設定に関係なく、プロセスが制限よりも多くのメモリを割り当てようとすると、カーネルはそれを拒否し、malloc()
はNULLを返します。最近のLinuxカーネル(2.6.xシリーズ全体を含む)では、常駐サイズ(setrlimit()
with _RLIMIT_RSS
_または_ulimit -m
_コマンド)の制限は効果がないことに注意してください。
以下のセッションは、カーネル2.6.32で4GB RAMおよび8GBスワップで実行されました。
_$ cat bigmem.c
#include <stdlib.h>
#include <stdio.h>
int main() {
int i = 0;
for (; i < 13*1024; i++) {
void* p = malloc(1024*1024);
if (p == NULL) {
fprintf(stderr, "malloc() returned NULL on %dth request\n", i);
return 1;
}
}
printf("Allocated it all\n");
return 0;
}
$ cc -o bigmem bigmem.c
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ Sudo bash -c "echo 2 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
2
$ ./bigmem
malloc() returned NULL on 8519th request
$ Sudo bash -c "echo 0 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ ulimit -v $(( 1024*1024 ))
$ ./bigmem
malloc() returned NULL on 1026th request
$
_
上記の例では、スワッピングまたはOOM killは発生しませんが、割り当てられたすべてのメモリをプロセスが実際に操作しようとした場合、状況は大きく変化します。
質問に直接回答するには:_ulimit -v
_コマンドで明示的に設定された仮想メモリの制限がない限り、マシンの物理リソースまたはアドレス空間の論理制限(32ビットシステムに関連)以外のヒープサイズ制限はありません。 glibcはヒープ上にメモリを割り当て続け、ヒープが大きくなるにつれてカーネルにますます要求します。最終的には、すべての物理メモリが使い果たされると、結果的にスワッピングが悪くなる場合があります。スワップ空間が使い果たされると、カーネルのOOMキラーによってランダムなプロセスが強制終了されます。
ただし、メモリの割り当ては、空きメモリの不足、断片化、または構成された制限に達するよりも多くの理由で失敗する可能性があることに注意してください。 glibのアロケータが使用するsbrk()
とmmap()
の呼び出しには、独自の失敗があります。プログラムブレークが別のすでに割り当てられているアドレス(例:共有メモリまたは以前にmmap()
でマップされたページ)に到達したか、プロセスのメモリマッピングの最大数を超えました。
あなたの元の問題は、malloc
が要求されたメモリをシステムに割り当てるのに失敗したことだったと思います。
これが発生した理由は、システムに固有です。
プロセスがロードされると、そのプロセスのシステムブレークポイントである特定のアドレスまでメモリが割り当てられます。そのアドレスを超えると、メモリはプロセスに対してマップ解除されます。したがって、プロセスが「ブレーク」ポイントに「ヒット」すると、システムからより多くのメモリが要求されます。これを行う1つの方法は、システムコール sbrk を使用することです。malloc
は内部でそれを実行しますが、何らかの理由でシステムで失敗しました。
たとえば、これには多くの理由が考えられます。
1)Linuxでは最大メモリサイズに制限があると思います。 ulimit
だと思います。たぶんあなたはそれにぶつかったでしょう。制限に設定されているかどうかを確認します
2)システムの負荷が高すぎる可能性があります
3)プログラムでメモリ管理が正しく行われず、メモリが断片化してしまうため、malloc
は要求されたチャンクサイズを取得できません。
4)プログラムがmalloc
内部データ構造を破壊しています。
等
これまでの回答に一点追加したいと思います。
アプリには、malloc()が「ソリッド」ブロックを返すという幻想があります。実際には、バッファは、RAMの多くのページに散らばって細かく存在する場合があります。ここでの重要な事実はこれです:プロセスの仮想メモリ、そのコードを含む、または大きな配列として何かを含む連続している必要があります。コードとデータが分離されていることを認めましょう。大きな配列char str [universe_size]は連続している必要があります。
さて、単一のアプリがヒープを任意に拡大して、そのような配列を割り当てることができるでしょうか?
マシンで他に何も実行されていない場合、答えは「はい」です。ヒープは途方もなく大きい場合がありますが、境界がなければなりません。ある時点で、sbrk()(Linuxでは、ヒープを「拡大」する関数)の呼び出しは、別のアプリケーション用に予約された領域でつまずくはずです。
この link は、いくつかの興味深い明確な例を提供しています。チェックしてください。 Linuxに関する情報は見つかりませんでした。