web-dev-qa-db-ja.com

「メモリ不足」は回復可能なエラーですか?

私は長い間プログラミングを行ってきましたが、表示されるプログラムは、メモリが不足すると、クリーンアップして終了しようとします。つまり、正常に失敗します。前回、実際に回復して正常に動作し続けようとしているのを見たのはいつだったか思い出せません。

特にガベージコレクションされた言語では、多くの処理がメモリを正常に割り当てることができることに依存しているため、メモリ不足エラーは回復不能として分類する必要があるようです。 (回復不能なエラーには、スタックオーバーフローなどが含まれます。)

それを回復可能なエラーにするための説得力のある議論は何ですか?

75
Walter Bright

それは本当にあなたが構築しているものに依存します。

Webサーバーが1つの要求/応答のペアに失敗しても、それ以降の要求を続行することはまったく不合理ではありません。ただし、単一障害がグローバル状態に悪影響を及ぼさないことを確認する必要があります。これは注意が必要です。ほとんどの管理対象環境(.NETやJavaなど)で障害が発生すると例外が発生することを考えると、例外が「ユーザーコード」で処理されると、将来のリクエストで回復できると思います。 1つの要求が10GBのメモリを割り当てようとして失敗した場合でも、システムの他の部分に害を及ぼすことはありません。ただし、要求をユーザーコードに渡そうとしているときにシステムのメモリが不足すると、そのようなことが厄介になる可能性があります。

36
Jon Skeet

ライブラリでは、ファイルを効率的にコピーする必要があります。これを行うと、通常、少数の大きなチャンクを使用してコピーする方が、多数の小さなチャンクをコピーするよりもはるかに効果的であることがわかります(たとえば、15'000をコピーするよりも15個の1MBチャンクをコピーする方が15MBファイルをコピーする方が高速です) 1Kチャンク)。

ただし、コードは任意のチャンクサイズで機能します。したがって、1MBのチャンクを使用すると高速になる可能性がありますが、多数のファイルがコピーされるシステム用に設計する場合は、OutOfMemoryErrorをキャッチし、成功するまでチャンクサイズを小さくすることをお勧めします。

もう1つの場所は、データベースに格納されているオブジェクトのキャッシュです。できるだけ多くのオブジェクトをキャッシュに保持したいが、アプリケーションの他の部分に干渉したくない。これらのオブジェクトは再作成できるため、メモリを節約してキャッシュをメモリ不足ハンドラーに接続し、アプリの残りの部分に十分な余裕ができるまでエントリを削除するのが賢明な方法です。

最後に、画像操作では、できるだけ多くの画像をメモリにロードする必要があります。繰り返しになりますが、OOMハンドラーを使用すると、ユーザーまたはOSがコードに付与するメモリの量を事前に知らなくても実装できます。

[編集]ここでは、アプリケーションに固定量のメモリが与えられており、この量がスワップスペースを除いた使用可能なメモリの合計よりも少ないという前提で作業していることに注意してください。非常に多くのメモリを割り当てることができ、その一部をスワップアウトする必要がある場合、私のコメントのいくつかはもはや意味がありません。

17
Aaron Digulla

MATLABのユーザーは、大きな配列で算術演算を実行すると、常にメモリが不足します。たとえば、変数xがメモリに収まり、「x + 1」を実行する場合、MATLABは結果にスペースを割り当ててから、結果を埋めます。割り当てが失敗した場合、MATLABエラーが発生し、ユーザーは別のことを試すことができます。このユースケースが発生するたびにMATLABが終了すると、災害になります。

9
Ben Hinkle

OOMから回復するための戦略はシャットダウンだけではないため、OOMは回復可能である必要があります。

実際には、アプリケーションレベルでのOOM問題に対するかなり標準的な解決策があります。アプリケーション設計の一環として、メモリ不足状態から回復するために必要なメモリの安全な最小量を決定します。 (例:ドキュメントの自動保存、警告ダイアログの表示、シャットダウンデータのログ記録に必要なメモリ)。

アプリケーションの開始時または重要なブロックの開始時に、その量のメモリを事前に割り当てます。メモリ不足状態を検出した場合は、ガードメモリを解放してリカバリを実行します。戦略はまだ失敗する可能性がありますが、全体としては大金に見合う価値があります。

アプリケーションをシャットダウンする必要がないことに注意してください。 OOM状態が解決されるまで、モーダルダイアログを表示できます。

私は100%確実ではありませんが、 ' Code Complete '(立派なソフトウェアエンジニアには必読)がこれをカバーしていると確信しています。

P.S.この戦略を支援するためにアプリケーションフレームワークを拡張できますが、そのようなポリシーをライブラリに実装しないでください(優れたライブラリはアプリケーションの同意なしにグローバルな決定を下しません)

8
Ifeanyi Echeruo

多くのことと同じように、それは費用便益分析だと思います。あなたcan malloc()障害からの回復を試みたプログラム-それは難しいかもしれませんが(あなたのハンドラーはそれが対処することを意図しているのと同じメモリ不足に陥らないほうがよいです)。

最も一般的なケースは、クリーンアップして正常に失敗することであることはすでに説明しました。その場合、正常に中止するコストは、回復における開発コストとパフォーマンスコストの組み合わせよりも低いと判断されました。

プログラムの終了が非常に費用のかかるオプションである状況の独自の例を考えることができると確信しています(生命維持装置、宇宙船の制御、長期的でタイムクリティカルな財務計算など)-最初の防衛線はもちろん、プログラムに予測可能なメモリ使用量があり、環境がそれを供給できることを確認するためです。

5
slim

IOキャッシュにメモリを割り当ててパフォーマンスを向上させるシステムに取り組んでいます。次に、OOMを検出すると、その一部を取り戻し、ビジネスロジックを続行できるようにします。 IOキャッシュが少なく、書き込みパフォーマンスがわずかに低いことを意味します。

また、ガベージコレクションを強制することでOOMを管理しようとし、オプションでプリフェッチまたはキャッシュされたデータなどの重要でないオブジェクトの一部を解放しようとする組み込みJavaアプリケーションを使用しました。

OOM処理の主な問題は次のとおりです。

1)発生した場所で再試行できる、またはロールバックしてより高いポイントから再試行できる。最近のほとんどのプログラムは、言語に依存しすぎてスローできず、最終的にどこに到達し、操作を再試行するかを実際に管理していません。保存するように設計されていない場合、通常、操作のコンテキストは失われます

2)実際にいくらかのメモリを解放できること。これは、どのオブジェクトが重要で何が重要でないかを知っている一種のリソースマネージャを意味し、システムは、解放されたオブジェクトが後で重要になったときに、それらを再要求できるようにします。

もう1つの重要な問題は、さらに別のOOM状況をトリガーせずにロールバックできることです。これは、高級言語では制御が難しいものです。

また、基盤となるOSはOOMに関して予測どおりに動作する必要があります。たとえば、Linuxは、メモリのオーバーコミットが有効になっている場合は機能しません。多くのスワップ対応システムは、問題のあるアプリケーションにOOMを報告するよりも早く死にます。

また、状況を引き起こしたのはプロセスではない場合があるため、問題のプロセスが引き続きリークしている場合は、メモリを解放しても役に立ちません。

これらすべての理由から、この手法を採用しているのは大規模な組み込みシステムであることがよくあります。OSとメモリを制御してそれらを有効にし、それらを実装するための規律/動機を持っているからです。

5
n-alexander

捕まえて正しく扱った場合にのみ回復可能です。

同じ場合、たとえば、リクエストが大量のメモリを割り当てようとしました。それは非常に予測可能であり、あなたはそれを非常にうまく扱うことができます。

ただし、マルチスレッドアプリケーションの多くの場合、OOEはバックグラウンドスレッド(システム/ 3サードパーティライブラリによって作成されたものを含む)でも発生する可能性があります。予測することはほとんど不可能であり、すべてのスレッドの状態を回復できない可能性があります。

4
Dennis C

いいえ。GCからのメモリ不足エラーは、通常、現在のスレッド内で回復できるはずがありません。 (ただし、回復可能なスレッド(ユーザーまたはカーネル)の作成と終了はサポートされている必要があります)

反例について:私は現在、GPUコンピューティングにNVIDIAのCUDAプラットフォームを使用するDプログラミング言語プロジェクトに取り組んでいます。 GPUメモリを手動で管理する代わりに、DのGCを活用するプロキシオブジェクトを作成しました。そのため、GPUがメモリ不足エラーを返した場合、完全な収集を実行し、2回目に失敗した場合にのみ例外を発生させます。しかし、これは実際にはメモリ不足の回復の例ではなく、GC統合の1つです。回復の他の例(キャッシュ、フリーリスト、自動縮小なしのスタック/ハッシュなど)はすべて、GCとは別のメモリを収集/圧縮する独自の方法を持ち、割り当てに対してローカルではない傾向がある構造です。関数。したがって、人々は次のようなものを実装するかもしれません:

T new2(T)( lazy T old_new ) {
    T obj;
    try{
        obj = old_new;
    }catch(OutOfMemoryException oome) {
        foreach(compact; Global_List_Of_Delegates_From_Compatible_Objects)
            compact();
        obj = old_new;
    }
    return obj;
}

これは、一般に、自己収集/圧縮オブジェクトの登録/登録解除のサポートをガベージコレクターに追加するための適切な議論です。

3
Robert Jacques

これは、メモリ不足の意味によって異なります。

ほとんどのシステムでmalloc()が失敗する場合、それはアドレス空間が不足しているためです。

そのメモリの大部分がキャッシュまたはmmapされた領域によって使用されている場合は、キャッシュを解放するか、マッピングを解除することで、メモリの一部を再利用できる可能性があります。ただし、これには、そのメモリを何に使用しているかを知っている必要があります。ほとんどのプログラムがそうではないか、違いがないことに気づきました。

自分でsetrlimit()を使用した場合(予期しない攻撃から保護するため、またはルートがあなたにそれを行った可能性があります)、エラーハンドラーの制限を緩和できます。私はこれを非常に頻繁に行います-可能であればユーザーにプロンプ​​トを表示し、イベントをログに記録した後。

一方、スタックオーバーフローをキャッチすることは少し難しく、移植性がありません。 [〜#〜] ecl [〜#〜] の可能性のあるソリューションを作成し、このルートを使用する場合はWindowsの実装について説明しました。数か月前にECLにチェックインされましたが、興味があれば元のパッチを掘り下げることができます。

1
geocar

質問には「言語にとらわれない」というタグが付けられていますが、言語や基盤となるシステムを考慮せずに回答することは困難です。 (私はいくつかの避難所を見る

メモリ割り当てが暗黙的であり、特定の割り当てが成功したかどうかを検出するメカニズムがない場合、メモリ不足状態からの回復は困難または不可能な場合があります。

たとえば、巨大な配列を割り当てようとする関数を呼び出す場合、ほとんどの言語は、配列を割り当てることができない場合の動作を定義しません。 (Adaでは、これにより、少なくとも原則として_Storage_Error_例外が発生し、それを処理できるはずです。)

一方、メモリの割り当てを試み、その失敗を報告できるメカニズム(Cのmalloc()やC++のnewなど)がある場合は、確かにそうです。その障害から回復することが可能です。少なくともmalloc()newの場合、割り当ての失敗はレポートの失敗以外のことは何もしません(たとえば、内部データ構造を破壊することはありません)。

回復を試みることが理にかなっているかどうかは、アプリケーションによって異なります。割り当てが失敗した後、アプリケーションが成功できない場合は、可能な限りのクリーンアップを実行して終了する必要があります。ただし、割り当ての失敗が1つの特定のタスクを実行できないことを意味するだけの場合、またはタスクをより少ないメモリでよりゆっくりと実行できる場合は、操作を続行するのが理にかなっています。

具体的な例:テキストエディタを使用しているとします。大量のメモリを必要とするエディタ内で操作を実行しようとして、その操作を実行できない場合は、エディタに、要求したことを実行できないことを通知してもらいたいそして編集を続けさせてください)。私の仕事を保存せずに終了することは容認できない反応でしょう。私の仕事を保存して終了する方が良いでしょうが、それでも不必要にユーザーに敵対的です。

1
Keith Thompson

一般的な場合、それは回復可能ではありません。

ただし、システムに何らかの形式の動的キャッシュが含まれている場合、メモリ不足ハンドラーは、キャッシュ内の最も古い要素(またはキャッシュ全体)をダンプすることがよくあります。

もちろん、「ダンプ」プロセスに新しいメモリ割り当てが必要ないことを確認する必要があります:)また、キャッシュダンプコードをアロケータに直接プラグインできない限り、失敗した特定の割り当てを回復するのは難しい場合があります。レベル。これにより、障害が呼び出し元に伝播されません。

1
Mike G.

特にガベージコレクション環境では、アプリケーションの高レベルでOutOfMemoryエラーをキャッチすると、多くのものがスコープ外になり、メモリを戻すために再利用できる可能性があります。

単一の過剰な割り当ての場合、アプリは問題なく動作し続けることができる場合があります。もちろん、メモリリークが徐々に発生する場合は、再び問題が発生する可能性があります(おそらく、後で発生する可能性が高くなります)が、アプリを正常に停止し、保存されていない変更をGUIアプリの場合など.

1

はい、OOMは回復可能です。極端な例として、UnixおよびWindowsオペレーティングシステムは、ほとんどの場合、OOM状態から非常にうまく回復します。アプリケーションは失敗しますが、OSは存続します(OSが最初に正しく起動するのに十分なメモリがあると仮定します)。

私はそれができることを示すためにこの例を引用するだけです。

OOMの扱いの問題は、実際にはプログラムと環境に依存します。

たとえば、多くの場合、OOMが発生する可能性が最も高い場所は、実際にOOM状態から回復するのに最適な場所ではありません。

これで、カスタムアロケータは、OOMを処理できるコード内の中心点として機能する可能性があります。 Javaアロケータは、実際にOOM例外をスローする前に、完全なGCを実行します。

アロケーターが「アプリケーションに対応」しているほど、OOMの中央ハンドラーおよびリカバリーエージェントとして適しています。 Javaをもう一度使用すると、アロケータは特にアプリケーションを認識しません。

これは、Javaのようなものがすぐにイライラするところです。アロケータをオーバーライドすることはできません。したがって、独自のコードでOOM例外をトラップすることはできますが、使用しているライブラリがあることは何も言えません。は適切にトラップするか、適切にOOM例外をスローします。一部のオブジェクトがnullに設定されて「発生しない」ため、OOM例外によって永久に破壊されるクラスを作成するのは簡単であり、回復することはできません。

ですから、はい、OOMは回復可能ですが、特にJavaのような現代の環境では、非常に困難な場合があり、さまざまな品質のサードパーティライブラリが多数あります。

1
Will Hartung

それを回復可能なエラーにするための説得力のある議論は何ですか?

Javaでは、notを回復可能なエラーにするための説得力のある引数は、JavaでOOMがanyいつでも、結果としてプログラムが一貫性のない状態になる可能性がある場合を含みます。したがって、OOMからの信頼できる回収は不可能です。つまり、OOM例外をキャッチした場合、プログラムの状態をrelyすることはできません。 VirtualMachineErrorのスロー保証なし を参照してください。

0
Raedwald

あなたが議論を求めたことは知っていますが、私は反対の議論しか見ることができません。

マルチスレッドアプリケーションでこれを実現する方法はとにかくわかりません。どのスレッドがメモリ不足エラーの実際の原因であるかをどのようにして知ることができますか? 1つのスレッドが常に新しいメモリを割り当て、ヒープの99%にgc-rootsを持つことができますが、失敗する最初の割り当ては別のスレッドで発生します。

実用的な例:Javaアプリケーション(JBossサーバーで実行中)でOutOfMemoryErrorが発生した場合、1つのスレッドが停止し、サーバーの残りの部分が実行を継続するわけではありません:いいえ、そこにはいくつかのOOMEであり、いくつかのスレッドを強制終了します(そのうちのいくつかはJBossの内部スレッドです)。プログラマーとして私がそれから回復するために何ができるか、あるいはJBossがそれから回復するために何ができるかさえわかりません。できるかどうかさえわかりません: Javadoc for VirtualMachineError は、そのようなエラーがスローされた後にJVMが「壊れた」可能性があることを示唆しています。しかし、おそらく質問は言語設計に向けられていました。

0
waxwing

今は困惑しているだけです。

職場では、アプリケーションのバンドルが連携して動作しており、メモリが不足しています。問題は、アプリケーションバンドルを64ビットにする(したがって、通常のWin32OSでの2Goの制限を超えて動作できるようにする)か、メモリの使用量を減らすことですが、この問題は「 OOMから回復する」は私の頭を止めません。

もちろん、私には解決策はありませんが、それでもC++を検索することで遊んでいます(主にRAIIと例外のため)。

おそらく、正常に回復するはずのプロセスは、回復の目的で予約された「メモリのバッファ/プール」を使用して、アトミック/ロールバック可能なタスクでの処理を中断する必要があります(つまり、強力な/スローなしの例外保証を提供する関数/メソッドのみを使用します)。

タスクの1つが失敗した場合、C++ bad_allocはスタックを巻き戻し、RAIIを介してスタック/ヒープメモリを解放します。その後、回復機能は可能な限り回収し(タスクの初期データをディスクに保存し、後で試行するために使用します)、おそらくタスクデータを後で試行するために登録します。

C++の強力な/スローしない保証を使用すると、メモリスワッピングに似ている場合でも(つまり、低速、やや応答がないなど)、使用可能なメモリが少ない状態でプロセスを存続させることができると思いますが、もちろんこれは唯一の理論。これをシミュレートする前に、このテーマについて賢くする必要があります(つまり、メモリが制限されたカスタムのnew/deleteアロケータを使用してC++プログラムを作成し、それらのストレスの多い条件下でいくつかの作業を試みます)。

上手...

0
paercebal

あなたが本当に記憶が不足しているなら、あなたはもう何も解放することができないので、あなたは運命にあります。

メモリが不足しているが、ガベージコレクタのようなものが起動して、まだ死んでいないメモリを解放できる場合。

もう1つの問題は断片化です。メモリが不足していない(断片化されている)場合でも、必要な巨大なチャンクを割り当てることができない場合があります。

0
robert.berger

uClibcには、動的に割り当てられるメモリがなくなったときにファイルI/O用に8バイト程度の内部静的バッファがあります。

0
Prof. Falken

ここにはすでに多くの良い答えがあります。しかし、私は別の視点で貢献したいと思います。

ほぼすべての再利用可能なリソースの枯渇は、一般的に回復可能であるはずです。その理由は、プログラムのすべての部分が基本的にサブプログラムであるということです。この時点で1つのサブが完了できないからといって、プログラムの状態全体がガベージであるとは限りません。駐車場が車でいっぱいだからといって、車をゴミ箱に捨てるわけではありません。ブースが無料になるまでしばらく待つか、クッキーを購入するために遠くの店に車で行きます。

ほとんどの場合、別の方法があります。エラーを回復不能にすることで、多くのオプションが効果的に削除されます。私たちの誰もが、私たちにできることとできないことを誰かに決めてもらうことを望んでいません。

同じことがディスクスペースにも当てはまります。それは本当に同じ理由です。そして、スタックオーバーフローについてのあなたのほのめかしに反して、私はそれがそして恣意的な制限であると言うでしょう。例外をスローして(多くのフレームをポップして)、別の効率の悪いアプローチを使用してジョブを実行できないようにする必要があるという正当な理由はありません。

私の2セント:-)

0
Zuu

これは難しい質問です。一見、メモリがなくなったということは「運が悪い」という意味のようですが、本当に主張すれば、メモリに関連する多くのものを取り除くことができることも確認する必要があります。他の方法で壊れた関数strtokを取り上げましょう。これは、一方ではメモリ関連の問題はありません。次に、Glibライブラリから対応するg_string_splitを取得します。これは、glibまたはGObjectベースのプログラムのほぼすべてのメモリの割り当てに大きく依存します。より動的な言語では、メモリ割り当てはより柔軟性のない言語、特にCの場合と同様に、はるかに多く使用されていると断言できます。しかし、代替案を見てみましょう。メモリが不足したときにプログラムを終了しただけでは、注意深く開発されたコードでも機能しなくなる可能性があります。ただし、回復可能なエラーがある場合は、それに対して何かを行うことができます。したがって、それを回復可能にするという議論は、その状況を異なる方法で「処理」することを選択できることを意味します(たとえば、緊急事態のためにメモリブロックを脇に置く、またはメモリの少ないプログラムに劣化させる)。

したがって、最も説得力のある理由はです。回復する方法を提供する場合は、回復を試すことができます。選択肢がない場合は、常に十分なメモリを取得する必要があります...

よろしく

0
Friedrich

メモリ不足は、空きメモリの枯渇、または不当に大きなブロック(1ギガなど)を割り当てようとしたことが原因である可能性があります。 「枯渇」の場合、メモリ不足はシステム全体で発生し、通常は他のアプリケーションやシステムサービスに影響を及ぼし、システム全体が不安定になる可能性があるため、忘れて再起動することをお勧めします。 「不当に大きなブロック」の場合、実際には不足は発生せず、続行しても安全です。問題は、現在のケースを自動的に検出できないことです。したがって、エラーを回復不能にして、このエラーが発生するたびに回避策を見つける方が安全です。プログラムで使用するメモリを少なくするか、場合によっては修正するだけです。メモリ割り当てを呼び出すコードのバグ。

0
sharptooth

通常、メモリ不足とは、何をしていたとしても終了する必要があることを意味します。ただし、クリーンアップに注意すると、プログラム自体が動作可能になり、他の要求に応答できるようになる可能性があります。 「申し訳ありませんが、メモリ不足です。シャットダウンします」と言うよりも、プログラムに「申し訳ありませんが、実行するのに十分なメモリがありません」と言う方がよいでしょう。

0
Loren Pechtel