new
演算子は実際に例外をスローできますか?
その場合、アプリケーションを強制終了する以外に、このような例外を処理するオプションはありますか?
更新:
現実のnew
- heavyアプリケーションは、メモリがないときに障害をチェックして回復しますか?
New演算子、およびnew []演算子はstd::bad_alloc
をスローする必要がありますが、動作が時々オーバーライドされる可能性があるため、これは常に当てはまるわけではありません。
std::set_new_handler
を使用できますが、突然std::bad_alloc
をスローすることとはまったく異なることが起こります。標準では、ユーザーがメモリを使用可能にするか、中止するか、std::bad_alloc
をスローする必要があります。しかし、もちろんそうではないかもしれません。
免責事項:私はこれを行うことを提案していません。
はい、new
は割り当てが失敗した場合にスローできます。これは、メモリが不足した場合、またはメモリブロックを割り当てすぎた場合に発生する可能性があります。
std::bad_alloc
例外および適切に処理します。これは理にかなっている場合もあれば、そうでない場合もあります(ほとんどの場合、読みません)。たとえば、巨大なバッファを割り当てようとしても、より少ないスペースで作業できる場合は、連続してより小さいブロックを割り当ててみてください。
仮想メモリなしでLinuxを実行している一般的な組み込みプロセッサで実行している場合、メモリを割り当てすぎると、新規が失敗する前にオペレーティングシステムによってプロセスが終了する可能性が非常に高くなります。
仮想メモリの最大値(標準Windowsでは2 GB)よりも少ない物理メモリを持つマシンでプログラムを実行している場合、利用可能な物理メモリにほぼ等しい量のメモリを割り当てると、さらに割り当てが成功することがわかります。しかし、ディスクへのページングが発生します。これによりプログラムが動かなくなり、実際に仮想メモリを使い果たすことができなくなる可能性があります。そのため、例外がスローされない場合があります。
仮想メモリよりも多くの物理メモリがあり、メモリの割り当てを続けると、要求しているブロックサイズを割り当てることができないポイントまで仮想メモリを使い果たしたときに例外が発生します。
さまざまなライフタイムで、小さなブロックを含む多くの異なるブロックサイズで割り当ておよび解放する長時間実行プログラムがある場合、仮想メモリは、新規が十分な大きさのブロックを見つけることができないポイントまで断片化する可能性があります要求を満たす。その後、newは例外をスローします。メモリリークが発生し、ランダムな場所に小さなブロックが漏れることがあり、その結果、メモリが任意の小さなブロックの割り当てが失敗するポイントまで断片化し、例外がスローされます。
誤って巨大な配列サイズをnew []に渡すプログラムエラーがある場合、newは失敗し、例外をスローします。これは、たとえば配列サイズが実際には何らかの初期化されていないメモリまたは破損した通信ストリームから派生した何らかのランダムバイトパターンである場合に発生する可能性があります。
上記はすべて、デフォルトのグローバルnewです。ただし、global newを置き換えて、クラス固有のnewを提供できます。これらもスローする可能性があり、その状況の意味は、プログラム方法によって異なります。 newには通常、要求されたメモリを取得するためのすべての可能な手段を試みるループを含めるのが普通です。それらがすべて使い果たされるとスローされます。あなたがすることはあなた次第です。
Newから例外をキャッチし、それが提供する機会を使用して、例外の前後のプログラムの状態を文書化できます。 「コアをダンプ」できます。プログラムの起動時に循環インストルメンテーションバッファが割り当てられている場合、プログラムを終了する前にディスクにダンプできます。プログラムの終了は正常に行うことができ、これは単に例外を処理しないよりも有利です。
私は、例外の後に追加のメモリを取得できる例を個人的に見たことがありません。ただし、1つの可能性は次のとおりです。メモリアロケータが非常に効率的であるが、空き領域の再利用が得意でないとします。たとえば、空き領域の断片化が発生する可能性があります。この場合、空きブロックは隣接しますが、合体しません。 new_handlerでキャッチされたnewからの例外を使用して、再試行の前に空き領域の圧縮手順を実行できます。
深刻なプログラムは、メモリを潜在的に不足しているリソースとして扱い、可能な限りその割り当てを制御し、可用性を監視し、何かが劇的に間違っているように見える場合は適切に対応する必要があります。たとえば、実際のプログラムでは、メモリアロケータに渡されるサイズパラメータにかなり小さな上限があり、これより大きな値を指定すると、リクエストを処理できるかどうかにかかわらず、何らかのエラー処理が発生する場合があります満足。長時間実行されるプログラムのメモリ増加率を監視する必要があると主張することができ、プログラムが近い将来利用可能なメモリを使い果たすと合理的に予測できる場合、プロセスの正常な再起動を開始する必要があります。
Unixシステムでは、システムのすべてのメモリを食い尽くさないように、メモリ制限を使用して(ulimit
を使用して)長時間実行されるプロセスを実行するのが一般的です。プログラムがその制限に達すると、std::bad_alloc
。
OPの編集の更新:メモリ不足状態から回復するプログラムの最も一般的なケースは、ガベージコレクションシステムであり、GCを実行して続行します。ただし、この種のオンデマンドGCは、実際には最終段階の作業のみを目的としています。通常、優れたプログラムは、コレクターへの負荷を軽減するために定期的にGCを試行します。
GC以外のプログラムがメモリ不足の問題から回復することはあまりありませんが、インターネットに接続しているサーバーの場合、回復する1つの方法は、「一時的な」エラーでメモリ不足を引き起こすリクエストを単に拒否することです。 (「先入れ先出し」戦略。)
osgx言った:
現実のアプリケーションは多くのニュースをチェックし、メモリがないときに回復できますか?
以前に my answer から this question で回答しました。
このような状況に対処することは非常に困難です。アプリケーションのユーザーに意味のあるエラーを返すこともできますが、メモリ不足が原因で問題が発生する場合は、エラーメッセージを割り当てるためのメモリを確保できないこともあります。それは本当にキャッチ22の状況です。
アプリケーションの起動時にメモリのチャンクを割り当てる防御的なプログラミング手法(メモリパラシュートまたは雨の日基金と呼ばれることもあります)があります。次にbad_alloc例外を処理すると、このメモリが解放され、使用可能なメモリを使用して、ユーザーに意味のあるエラーを表示するなど、アプリケーションを正常に終了します。これはクラッシュするよりもはるかに良いです:)
コンパイラ/ランタイムとoperator new
使用している(たとえば、特定のバージョンのVisual Studio 箱から出さない が、代わりにNULL
ポインターを返す代わりにmalloc
を返します。 )
catch
a std::bad_alloc
例外、または 明示的にnothrow new
スローする代わりにNULL
を返します。 ( 過去のStackOverflowの投稿 件名の周りの回転も参照してください。)
ご了承ください operator new
、malloc
など、willは、メモリ不足、アドレス空間不足(たとえば、 32ビットプロセス(OSに依存)、クォータを超えている(ulimit
が既に言及されている)、または連続したアドレススペースを超えている(断片化されたヒープなど)
すべてのnew
で例外を処理する必要はありません:)例外は伝播できます。エラーを処理する各「モジュール」に特定のポイントがあるようにコードを設計します。
はい、new
はstd::bad_alloc
(std::exception
のサブクラス)をスローできますが、これはキャッチできます。
この例外を絶対に避けたい場合で、代わりにnew
の結果をNULLポインターでテストする準備ができている場合は、nothrow
引数を追加できます。
T* p = new (nothrow) T(...);
if (p == 0)
{
// Do something about the bad allocation!
}
else
{
// Here you may use p.
}
はいnew
は、使用可能なメモリがなくなると例外をスローしますが、すべての新しいを_try ... catch
_でラップする必要があるという意味ではありません。プログラムが実際にそれについて何かできる場合にのみ、例外をキャッチしてください。
プログラムがその例外的な状況を処理するために何もできない場合、メモリを使い果たした場合によくあることですが、例外をキャッチすることは役に立ちません。合理的に実行できるのがプログラムを中止することだけである場合は、例外をトップレベルまでバブルするだけで、プログラムも終了します。
Windowsでは、非常に大きなnew/mallocsが仮想メモリからのみ割り当てることに注意してください。実際には、例外が発生する前にマシンがクラッシュします。
char *pCrashMyMachine = new char[TWO_GIGABYTES];
あえて試してみてください!
多くの場合、メモリ不足の状況に対して合理的な回復はありません。その場合、アプリケーションを終了させることはおそらく完全に合理的です。コンパイラーがデフォルトで提供するよりも優れたエラーメッセージを表示するには、例外を高レベルでキャッチする必要があるかもしれませんが、それでも動作させるためにいくつかのトリックを再生する必要があります(プロセスが非常に低い可能性が高いためその時点でのリソース)。
処理および回復できる特別な状況がない限り、例外を処理しようとして多大な労力を費やす必要はおそらくないでしょう。
私はMac OS Xを使用していますが、malloc
return NULL
(C++ではnew
からの例外を意味する)を見たことはありません。マシンは動きを止め、減少するメモリをプロセスに割り当てるために最善を尽くし、最後にSIGSTOPを送信し、ユーザーにプロセスを割り当てエラーに対処させるのではなく、強制終了させます。
ただし、それは1つのプラットフォームにすぎません。確かに、デフォルトのアロケーターがスローするプラットフォームがあります。そして、Chrisが言うように、ulimit
は人為的な制約を導入するかもしれないので、例外は期待される振る舞いになるでしょう。
また、デフォルトのone/malloc
以外にもアロケーターがあります。クラスが_operator new
_をオーバーライドする場合、カスタム引数をnew(…)
に使用するか、アロケーターオブジェクトをコンテナーに渡すと、おそらく_bad_alloc
_をスローする独自の条件を定義します。
New-handler関数は、メモリを新たに割り当てる試みが失敗するたびに、割り当て関数によって呼び出される関数です。独自のロギングまたは特別なアクション、たとえばメモリの追加などを行うことができます。その目的は次の3つのうちのいずれかです。1)メモリをさらに使用可能にする2)プログラムを終了する)std :: bad_alloc型の例外またはstd :: bad_allocから派生した例外をスローします。デフォルトの実装はstd :: bad_allocをスローします。ユーザーは独自の新しいハンドラを持つことができます。これにより、デフォルトとは異なる動作が提供される場合があります。これは、本当に必要な場合にのみ使用してください。詳細とデフォルトの動作については例を参照してください。
#include <iostream>
#include <new>
void handler()
{
std::cout << "Memory allocation failed, terminating\n";
std::set_new_handler(nullptr);
}
int main()
{
std::set_new_handler(handler);
try {
while (true) {
new int[100000000ul];
}
} catch (const std::bad_alloc& e) {
std::cout << e.what() << '\n';
}
}
はい、新しいことができます。
「実際の」プログラムについて質問しているので、私は20年以上にわたり、さまざまなシュリンクラップされた商用ソフトウェアアプリケーションに取り組んできました。数百万人のユーザーを抱える「本物の」プログラム。すぐに購入して棚で購入できること。はい、新しいものは投げることができます。
これを処理するにはさまざまな方法があります。
まず、独自のnew_handlerを作成します(これは、newが放棄してスローされる前に呼び出されます-set_new_handler()関数を参照)。 new_handlerが呼び出されたら、本当に必要のないものを解放できるかどうかを確認します。また、メモリが不足していることをユーザーに警告します。 (はい、あなたが本当に低い場合、ユーザーに何かを警告するのは難しいかもしれません)。
1つは、プログラムのstartに「余分な」メモリを事前に割り当てることです。メモリが足りなくなったら、この追加メモリを使用して、ユーザーのドキュメントのコピーをディスクに保存します。その後、警告して、おそらく終了します。
等これは単なる概要であり、明らかにそれ以上のものがあります。
低メモリの処理は簡単ではありません。
リソースを制限する決定により、最も現実的に新しいものがスローされます。このクラス(メモリを集中的に使用する場合があります)が物理プールからメモリを取り出し、多くのオブジェクトがそれから取り出す場合(サウンド、テクスチャなどのその他のもののためにメモリが必要な場合)、後でクラッシュする代わりにスローする場合がありますメモリを割り当てることができます。 (奇妙な副作用のように見えます)。
Newのオーバーロードは、メモリが制限されているデバイスで役立ちます。ハンドヘルドやコンソールなど、クールなエフェクトを使いすぎて船外に出るのが簡単な場合。
アプリケーション/サービス/システムを侵害する試みを意味する可能性があるため、外部から与えられた何かに基づいてメモリを割り当てる場合、この例外をチェック/キャッチすることをお勧めします。起こる。
実行時の要求を満たすのに十分なメモリがプールにない場合、new演算子はstd :: bad_alloc例外をスローします。
これは、設計が悪い場合、または割り当てられたメモリが正しく解放されない場合に発生する可能性があります。
そのような例外の処理は設計に基づいており、1つの方法は一時停止してからしばらくしてから再試行し、プールにより多くのメモリが返されてリクエストが成功することを期待します。