75

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

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

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

4

24 に答える 24

37

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

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

于 2008-12-02T12:04:54.207 に答える
17

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

ただし、コードは任意のチャンク サイズで機能します。したがって、1MB のチャンクの方が高速かもしれませんが、大量のファイルがコピーされるシステム用に設計する場合は、OutOfMemoryError をキャッチして、成功するまでチャンク サイズを減らすのが賢明かもしれません。

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

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

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

于 2008-12-02T12:16:20.613 に答える
9

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

于 2008-12-02T16:08:36.493 に答える
8

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

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

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

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

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

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

于 2009-01-03T22:37:51.073 に答える
5

多くのことと同様に、これは費用対効果の分析だと思います。malloc() の失敗からの回復を試みるプログラムを作成することはできますが、それは難しいかもしれません (ハンドラーは、対処するのと同じメモリ不足に陥らない方がよいでしょう)。

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

プログラムの終了が非常に高価なオプションである状況の例をあなた自身で思いつくことができると確信しています (生命維持装置、宇宙船の制御、長時間実行されタイムクリティカルな財務計算など) - 防御の第一線はもちろん、プログラムが予測可能なメモリ使用量を持ち、環境がそれを提供できるようにするためです。

于 2008-12-02T12:08:28.087 に答える
5

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

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

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

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

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

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

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

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

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

于 2008-12-02T12:18:56.567 に答える
4

キャッチして正しく処理した場合にのみ回復可能です。

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

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

于 2008-12-02T12:38:31.410 に答える
3

いいえ。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;
}

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

于 2008-12-02T15:26:12.540 に答える
1

一般的なケースでは、回復できません。

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

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

于 2008-12-02T12:03:27.270 に答える
1

The question is tagged "language-agnostic", but it's difficult to answer without considering the language and/or the underlying system. (I see several toher hadns

If memory allocation is implicit, with no mechanism to detect whether a given allocation succeeded or not, then recovering from an out-of-memory condition may be difficult or impossible.

For example, if you call a function that attempts to allocate a huge array, most languages just don't define the behavior if the array can't be allocated. (In Ada this raises a Storage_Error exception, at least in principle, and it should be possible to handle that.)

On the other hand, if you have a mechanism that attempts to allocate memory and is able to report a failure to do so (like C's malloc() or C++'s new), then yes, it's certainly possible to recover from that failure. In at least the cases of malloc() and new, a failed allocation doesn't do anything other than report failure (it doesn't corrupt any internal data structures, for example).

Whether it makes sense to try to recover depends on the application. If the application just can't succeed after an allocation failure, then it should do whatever cleanup it can and terminate. But if the allocation failure merely means that one particular task cannot be performed, or if the task can still be performed more slowly with less memory, then it makes sense to continue operating.

A concrete example: Suppose I'm using a text editor. If I try to perform some operation within the editor that requires a lot of memory, and that operation can't be performed, I want the editor to tell me it can't do what I asked and let me keep editing. Terminating without saving my work would be an unacceptable response. Saving my work and terminating would be better, but is still unnecessarily user-hostile.

于 2013-08-05T19:02:26.717 に答える
1

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

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

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

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

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

于 2008-12-02T13:05:09.233 に答える
1

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

単一の過剰な割り当ての場合、アプリは問題なく動作し続けることができる場合があります。もちろん、メモリ リークが徐々に発生している場合は、問題が再び発生するだけです (遅かれ早かれ可能性が高い)。ただし、アプリを適切に停止する機会を与え、未保存の変更をGUIアプリの場合など

于 2009-01-03T23:28:15.670 に答える
1

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

この例を引用するのは、それが可能であることを示すためだけです。

OOM を扱う際の問題は、プログラムと環境に大きく依存します。

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

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

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

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

そうです、OOM は回復可能ですが、特に Java のような最新の環境では非常に困難な場合があり、さまざまな品質のサードパーティ ライブラリが大量に存在します。

于 2009-01-03T23:33:38.810 に答える
0

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

于 2009-02-03T08:06:09.427 に答える
0

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

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

ほとんどの場合、別の方法があります。エラーを回復不能にすることは、事実上多くのオプションを削除します。また、できることとできないことを誰かに決めてもらいたいと思う人はいません。

同じことがディスク容量にも当てはまります。本当に同じ理屈です。そして、スタックオーバーフローは回復不能であるというあなたのほのめかしに反して、それは恣意的な制限だと思います。例外をスロー (大量のフレームをポップ) してから、別の非効率的な方法を使用してジョブを完了させてはならないという正当な理由はありません。

私の2セント:-)

于 2009-02-21T19:26:26.110 に答える
0

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

Java では、回復可能なエラーにしないための説得力のある議論は、Java ではいつでもOOM を通知できるためです。これには、結果としてプログラムが矛盾した状態に入る可能性がある場合も含まれます。したがって、OOM からの信頼できる回復は不可能です。OOM 例外をキャッチすると、プログラムの状態に依存できなくなります。非スロー VirtualMachineError の保証を参照してください 。

于 2012-01-04T18:24:58.820 に答える
0

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

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

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

于 2009-11-09T21:33:08.317 に答える
0

これは難しい質問です。一見すると、これ以上メモリがないということは「運が悪い」ことを意味するように見えますが、本当に主張すれば、多くのメモリ関連のものを取り除くことができることも確認する必要があります。一方ではメモリ関連の問題がない壊れた関数 strtok を別の方法で考えてみましょう。次に、Glib ライブラリの対応する g_string_split を使用します。これは、glib または GObject ベースのプログラムのほぼすべてがメモリの割り当てに大きく依存しています。より動的な言語では、より柔軟性のない言語、特に C の場合と同様に、メモリ割り当てがはるかに使用されると断言できます。しかし、代替案を見てみましょう。メモリ不足になったときにプログラムを終了するだけでは、慎重に開発されたコードでも動作しなくなる可能性があります。ただし、回復可能なエラーが発生した場合は、それに対して何かを行うことができます。したがって、引数は、

というわけで、一番の理由は。回復する方法を提供する場合は、回復を試すことができます。選択肢がない場合は、常に十分なメモリを確保する必要があります...

よろしく

于 2008-12-02T12:05:09.887 に答える
0

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

于 2009-01-03T23:35:41.253 に答える
0

それは今私を困惑させているだけです。

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

もちろん、解決策はありませんが、C++ 用の解決策を探しています (主に RAII と例外のため)。

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

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

C++ のストロング/ノスロー保証の使用は、たとえそれがメモリ スワッピングに似ていたとしても (つまり、遅い、やや応答しないなど)、プロセスが利用可能なメモリが少ない状態で生き残るのに役立つと信じていますが、もちろん、これは理論だけ。これをシミュレートする前に、この件についてもっと賢くする必要があります (つまり、メモリが限られているカスタムの新規/削除アロケータを使用して C++ プログラムを作成し、ストレスの多い条件下でいくつかの作業を試みます)。

良い...

于 2008-12-11T21:35:45.063 に答える
0

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

于 2010-01-13T15:31:16.700 に答える
0

本当にメモリが不足している場合は、もう何も解放できないため、運命にあります。

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

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

于 2009-05-18T19:50:57.753 に答える
-1

私はこれを持っています:

void *smalloc(size_t size) {
  void *mem = null; 
  for(;;) {
   mem = malloc(size);
   if(mem == NULL) {
    sleep(1);
   } else 
     break;
  }
  return mem;
}

これは、システムをすでに数回保存しています。現在メモリが不足しているからといって、システムの他の部分やシステムで実行されている他のプロセスに、すぐに返されるメモリがあるとは限りません。そのようなトリックを試みる前に非常に注意を払い、プログラムで割り当てるすべてのメモリを完全に制御する必要があります。

于 2009-08-07T13:40:57.750 に答える