malloc
の割り当てが失敗した場合、再試行する必要がありますか?
このようなもので:
char* mystrdup(const char *s)
{
char *ab = NULL;
while(ab == NULL) {
ab=(char*)malloc(strlen(s)+1);
}
strcpy(ab, s);
return ab;
}
Whileループはメモリ割り当てをチェックするのに有効ですか?
一般に、最新のmalloc()
実装は絶対的な最後の手段としてのみNULL
を返し、再試行してもまったく役に立ちません。役立つ唯一のことはメモリを解放するそして再試行することです。アプリケーションが消費可能なリソースを保持している場合は、それらを解放してから、もう一度試してみてください。
一部の環境では、少量のメモリを雨の日の資金として割り当てるのが便利な方法です。 malloc()
ever does return NULL
の場合、その雨の日の資金を解放し、エラーを処理して終了するために必要なリソースを割り当てることができます。優雅に。これは、古いMacintoshToolboxを使用してプログラミングする場合の一般的な方法でした。 malloc()
がNULL
を返した場合、そのスペースを使用して、終了する前に問題を報告するダイアログを作成できます。
シングルスレッドプログラムでは、試行の間にメモリを解放せずに「再試行」しても、実用的な意味はありません。永遠にループします。
マルチスレッドプログラムでは、並行して実行されている別のスレッドが突然、自身のメモリの一部を解放することを決定した場合、これは「機能する」可能性があります。このような場合のループは、従来の「ビジー待機」ループを構成します。しかし、この場合でも、そのようなコードは、1つ以上の理由で実用的な価値がほとんどありません。
いいえ、決して。 malloc
がNULLを返す場合、それはエラーを示しており、おそらく中止する必要があります。
これが役立つ理由や時期について議論することなく、少なくとも64ビットコードとデフォルトのページファイル設定を備えたWindowsでは、ループ内での再割り当ての試みが機能する可能性があります。さらに、これにより、驚くほど多くの仮想メモリが追加される可能性があります。ただし、これを無限ループで実行するのではなく、有限回数の再試行を使用してください。証拠として、1Mbのメモリのリークをシミュレートする次のコードを試してください。リリースビルドで実行する必要があります。できればデバッガの下では実行しないでください。
for (int i = 0; i < 10; i++)
{
size_t allocated = 0;
while (1)
{
void* p = malloc(1024 * 1024);
if (!p)
break;
allocated += 1;
}
//This prints only after malloc had failed.
std::cout << "Allocated: " << allocated << " Mb\n";
//Sleep(1000);
}
8GbのRAMおよびシステム管理のページファイルを備えた私のマシンでは、次の出力が得られます(VS2013 for x64ターゲットでビルドされ、Windows 7 Proでテストされています):
Allocated: 14075 Mb
Allocated: 16 Mb
Allocated: 2392 Mb
Allocated: 3 Mb
Allocated: 2791 Mb
Allocated: 16 Mb
Allocated: 3172 Mb
Allocated: 16 Mb
Allocated: 3651 Mb
Allocated: 15 Mb
このような動作の正確な理由はわかりませんが、ページファイルのサイズ変更がリクエストに対応できなくなると、割り当てが失敗し始めるようです。私のマシンでは、このループの後、ページファイルは8Gbから20Gbに増加しました(プログラムが終了すると8 Gbに戻ります)。
これがあなたが望むことをすることは信じられないほどありそうにありません。メモリが不足している場合は、より多くを取得するまでビジーループを実行すると、がっかりする可能性があります。不要になったメモリを解放するか、エラーを返すことによってリソースの枯渇に対処できるように、呼び出し元のプログラムにNULLを返す必要があります。
私の経験(UNIX)では、ネットワーク管理者が「必要な新しいサーバーがすでに存在し、準備ができている」と自慢した直後に、malloc障害を堅牢に処理することが最も重要です。典型的な返事は次のようなものです:「うわー、それは速かった、ありがとう」。この時点で、mallocはNULLを返します。
最善の方法は、std :: bad_alloc例外をスローし、静的エラーメッセージをメイン()でキャッチして表示することです。うまくいけば、デストラクタは、mainでのエラー処理が失敗しないように、メモリを解放するのに十分なクリーンアップを実行します。
適切なOSとプログラミングスタイルを使用している場合、OOM状態は回復可能です。
ヒープサイズを増やしてみてください(動的割り当て用に取っておいたメモリ)。
ソフトウェアの目的とコードのどの部分が影響を受けるかによって異なります。
最初に知っておくと、malloc()は、使用可能なページがない場合に失敗する可能性があります。アプリケーションが制限に達した場合、ループは機能しませんが、システムのメモリが不足した場合は試してみる価値がありますが、無限のループを回避する必要があります。驚き!オペレーティングシステムが一時的に応答してRAMを割り当てることができなかった場合は、完全に正常です。
とにかくこれは非常に良い質問です
同様の問題はSIGNALSのキャプチャではなく、マルチスレッドまたは非同期TCPサーバーがクライアント接続を中止した場合、ソフトウェアはSIGPIPEによって終了します。これは正常ですが、プログラムを終了させることになりますが、そうすべきではありません。 。これを防ぐには、シグナルをフックする必要があります。
実世界の例では(私自身)。
mallocが失敗し、コードの一部にのみ影響する場合
私は、新しい接続がデータを送信するときにmalloc()またはnew []を使用し、受信したデータをバッファに保存しました。mallocまたはreallocが失敗すると、関数はfalseとして返され、バッファを解放します。接続はエラー(ランジェリー)でドロップされます。ソフトウェアは引き続き実行されますが、1つの接続が切断された場合。これは正しい方法だと思います。
mallocがソフトウェアアボートを引き起こさなければならない場合
私はmalloc()を使用して、コアを定義する配列や構造などの重要なデータ用のスペースを作成しました。これは通常、ソフトウェアの最初にinitセクションとして実行されます。malloc()が失敗した場合、ソフトウェアは中止して終了する必要があります。エラーコード。すべての操作は、データを入力する必要のあるテーブルに依存するためです。 (ビルトインファイルシステム)
mallocが再試行できる場合
業界をリードするタイプ(高可用性)のデータロガーソフトウェアがありました。malloc()が失敗した場合、mutex_lock()をトリガーすると、バックエンド側でソフトウェアがフリーズし、mallocプロシージャをX秒間再試行します。 malloc()が引き続き失敗し、すべてのスレッドでデストラクタの呼び出しを開始し、完全なシャットダウンを実行する場合。ただし、この時点でmalloc()を試行しても失敗する場合は、malloc()が成功してスタック呼び出しを終了するという2つのオプションがあります。最後のスレッドを終了するか、ロールバックを実行して最後のスレッドを終了します。
発生しているときはいつでも、ソフトウェアも終了しません。最初から始めてみてください。
たぶん言及する価値があります..
私は何年も前にまったく同じジレンマを抱えていました。何かが私のソフトウェアのメモリリークを引き起こし、私のRAMをすべて食べましたが、現在の状態を保存するために、多くのmalloc( )、解決策は、これが発生したときにすべてのスレッドを閉じてデストラクタを呼び出し、データを保存することでしたが、興味深いのは、すべての接続を閉じてソケット、ssl_ctxを解放したときです...メモリ消費量は数日後に128KBに低下しました満足のいくデバッグの結果、SSL_CTXには内部ストレージとキャッシュがあることがわかりました。オンラインで接続がない場合は、SSL_CTXを解放して、魅力のように機能します。
[〜#〜] summ [〜#〜]
ご覧のとおり、これは芸術であり、malloc()を使用してやりたいことを実行します。malloc()が失敗した場合に実行する必要のある本や標準はありません。誰かがあなたが何をすべきかをあなたに言うなら、それは彼の意見だけです。
無限ループを回避するための私の好ましい方法
PSEUDO CODE:
var ts = TIME()
var max_seconds = 5
var success = true
WHILE (MALLOC() == FAIL) DO
IF TS + max_seconds < TIME() THEN
success = false
BREAK
END
SLEEP(100ms)
END
malloc()は、メモリを割り当てるために最善を尽くします。失敗した場合は、whileループでメモリの割り当てを再試行する代わりに(プログラムが永久にスタックする可能性があります)、可能であれば、他のプロセスまたはスレッドによって保持されているメモリを解放してから再試行してください。
もう1つの方法は、コード自体の中から(ただし危険で望ましくない)、その場でスワップファイルまたはページングメモリを増やすことによってメモリを増やすか、手動で行うことです。
このような問題を回避する最善の方法は、コード自体を記述しながら、メモリ要件を計算または推定することです。