web-dev-qa-db-ja.com

JavaでOutOfMemoryErrorを処理する方法は?

約100万個のアイテムをシリアル化する必要があり、コードを実行すると次の例外が発生します。

Exception in thread "main" Java.lang.OutOfMemoryError: Java heap space
    at Java.util.Arrays.copyOfRange(Unknown Source)
    at Java.lang.String.<init>(Unknown Source)
    at Java.io.BufferedReader.readLine(Unknown Source)
    at Java.io.BufferedReader.readLine(Unknown Source)
    at org.girs.TopicParser.dump(TopicParser.Java:23)
    at org.girs.TopicParser.main(TopicParser.Java:59)

これをどのように処理しますか?

12
shilpa

理想的には、使用するメモリが少なくなるようにコードを再構築します。たとえば、すべてをメモリに保持する代わりに、出力をストリーミングすることができます。

または、-Xmxオプションを使用してJVMにメモリを追加します。

28
Kieron

公式のJavaの答えは、「ああ、メモリ不足です!私は屈服します!」です。これは、メモリ不足が発生しない環境でプログラミングした人にとっては、かなり苛立たしいことです。致命的なエラーになる可能性があります(たとえば、OSの作成、保護されていないOS用のアプリの作成)。

降伏する意欲が必要です-Javaメモリ割り当てのすべての側面を制御することはできないため、プログラムが低メモリ状態で成功することを保証することはできません。しかし、それは保証されません。あなたは戦いなしで降りなければならないことを意味します。

ただし、戦う前に、その必要性を回避する方法を探すことができます。おそらく、Javaシリアル化を回避し、代わりに、作成に大きなメモリ割り当てを必要としない独自のデータ形式を定義できます。シリアル化は、以前に見たオブジェクトの記録を保持するため、大量のメモリを割り当てます。 、再び発生した場合、再度出力する代わりに番号で参照できるようにします(これにより、無限ループが発生する可能性があります)。ただし、これは汎用である必要があるためです。データ構造によっては、次のことができる場合があります。余分な状態を保存する必要がほとんどなく、ストリームに書き込むことができるテキスト/バイナリ/ XML /その他の表現を定義します。または、必要な追加の状態を作成せずに、オブジェクトに保存するように調整できる場合があります。シリアル化時。

アプリケーションが大量のメモリを使用する1つの操作を実行するが、ほとんどの場合使用量がはるかに少ない場合、特にその操作がユーザーによって開始される場合、および使用するメモリを少なくする方法や使用可能なメモリを増やす方法が見つからない場合は、 OutOfMemoryをキャッチする価値があるかもしれません。問題が大きすぎることをユーザーに報告し、問題を切り詰めて再試行するようにユーザーを招待することで、回復できます。彼らが問題の設定に1時間費やしただけの場合、あなたはそうしますnotプログラムから抜け出し、すべてを失いたい-あなたは彼らにそれについて何かをする機会を与えたいのです。エラーがスタックの上位でキャッチされている限り、エラーがキャッチされるまでに過剰なメモリは参照されないため、VM少なくとも回復する機会が与えられます。必ずキャッチしてください。通常のイベント処理コードの下にエラーがあります(通常のイベント処理でOutOfMemoryをキャッチすると、ユーザーにダイアログを表示しようとしてもメモリが不足していて、別のエラーがキャッチされるため、ループがビジーになる可能性があります)。メモリホッグとして識別した操作の周囲で、メモリホッグ以外のコードに由来する処理できないOutOfMemoryErrorsがキャッチされないようにします。

非対話型アプリでも、失敗した操作を放棄することは理にかなっているかもしれませんが、プログラム自体が実行を継続し、さらにデータを処理します。これが、Webサーバーが複数のプロセスを管理し、メモリ不足のために1つのページ要求が失敗した場合に、サーバー自体がフォールオーバーしないようにする理由です。冒頭で述べたように、単一プロセスJavaアプリはそのような保証を行うことはできませんが、少なくともデフォルトよりも少し堅牢にすることができます。

そうは言っても、あなたの特定の例(シリアル化)はこのアプローチの良い候補ではないかもしれません。特に、問題があると言われたときにユーザーが最初にやりたいことは、作業を保存することです。ただし、シリアル化が失敗している場合は、保存できない可能性があります。それはあなたが望むものではないので、いくつかの実験や計算を行い、プログラムが許可する数百万のアイテムを手動で制限する必要があるかもしれません(実行中のメモリの量に基づいて)、シリアル化を試みるポイント。

これは、エラーをキャッチして続行するよりも堅牢ですが、残念ながら正確な境界を計算するのは難しいため、おそらく注意を怠る必要があります。

デシリアライズ中にエラーが発生している場合は、はるかに確固たる立場にあります。ファイルをloadに失敗しても、回避できれば、アプリケーションで致命的なエラーになることはありません。エラーをキャッチすることが適切である可能性が高くなります。

リソースの不足を処理するために何をするにしても(エラーでアプリを停止させるなど)、結果を気にする場合は、徹底的にテストすることが非常に重要です。難しいのは、コードのどのポイントで問題が発生するかを正確に知ることができないため、通常、テストする必要のあるプログラムの状態が非常に多いことです。

27
Steve Jessop

コードで処理しないでください。 OutOfMemoryをキャッチして処理しないでください。代わりに、より大きなヒープスペースでJVMを開始します

Java -Xmx512M

トリックを行う必要があります。

詳細については、 here を参照してください

12
Mo.

他の誰もがJavaより多くのメモリを与える方法をすでにカバーしていますが、「ハンドル」はおそらくキャッチを意味する可能性があるので、Sunが言っていることを引用します Error s:

ErrorThrowableのサブクラスであり、妥当なアプリケーションがキャッチしようとしてはならない重大な問題を示します。そのようなエラーのほとんどは異常な状態です。

(私の強調)

9
Powerlord

プログラムがJVMで使用可能なメモリよりも多くのメモリを必要とするため、OutOfMemoryErrorが発生します。これを支援するために実行時に具体的にできることは何もありません。

Krosenvoldが指摘しているように、アプリケーションは適切なメモリ要求を行っている可能性がありますが、JVMが十分に起動されていないことがあります(たとえば、アプリのピークメモリフットプリントは280MBですが、JVMは256MBでしか起動しません)。この場合、割り当てられるサイズを増やすとこれが解決されます。

起動時に十分なメモリを供給していると思われる場合は、アプリケーションが一時的に大量のメモリを使用しているか、メモリリークが発生している可能性があります。あなたが投稿した状況では、潜在的にそれらを順番に扱っているとしても、一度にメモリ内の百万のアイテムすべてへの参照を保持しているように聞こえます。

「完了」したアイテムの参照がどのようなものかを確認します。ガベージコレクションできるように、できるだけ早くこれらを尊重する必要があります。たとえば、コレクションに100万個のアイテムを追加してからそのコレクションを反復処理する場合、それらのオブジェクトインスタンスをすべて保存するのに十分なメモリが必要になります。代わりに、一度に1つのオブジェクトを取得し、それをシリアル化してから参照を破棄できるかどうかを確認してください。

これを解決するのに問題がある場合は、擬似コードスニペットを投稿すると役立ちます。

7
Andrzej Doyle

与えられたいくつかのヒントに加えて、メモリが不足していることを確認し、より多くのメモリ(-Xmx512M)でJVMを起動します。 OutOfMemoryErrorが原因で、TopicParserがおそらくかなり大きい行を読み取っているように見えます(これは避けるべきものです)。FileReader(または、エンコーディングに問題がある場合は、InputStreamReaderラッピングFileInputStream)。そのread(char [])メソッドを合理的にサイズのchar []配列をバッファーとして使用します。

また、最後に、OutOfMemoryErrorがなぜあるのかを少し調査するために、JVMで-XX:+ HeapDumpOnOutOfMemoryErrorフラグを使用して、ディスクへのダンプヒープ情報を取得できます。

幸運を!

3

興味深い-readlineのメモリが不足しています。推測では、改行なしで大きなファイルを読み込んでいます。

Readlineを使用して、ファイルから1つの大きな長い文字列としてデータを取得する代わりに、入力を少しよく理解し、チャンクで処理するものを記述します。

単純に必須ファイル全体を1つの大きな長い文字列に入れる場合...まあ、コーディングが上手になります。一般に、mutimegabyteデータをすべて単一のバイト配列(またはその他)に詰め込んで処理しようとすることは、失うのに良い方法です。

CharacterSequenceをご覧ください。

2
Paul Murray

一時キーワードを使用して、既存のデータから生成できるシリアル化されたクラスのフィールドをマークします。 writeObjectとreadObjectを実装して、一時データの再構築を支援します。

2
Martin OConnor

(-Xmxを介して)ヒープスペースを増やすという提案に従った後、アプリケーションのメモリ使用量をプロファイルするには、必ず JConsole または JVisualVM のいずれかを使用してください。メモリ使用量が継続的に増加しないことを確認してください。その場合でもOutOfMemoryExceptionが発生しますが、時間がかかるだけです。

1
Brendan Cashman

私は、例外からメモリをキャッチしようとすべきではない他のすべての見解を尊重して、別の方法を発見しました。これは私が最近学んだことです。

catch (Throwable ex){
 if (!(ex instanceof ThreadDeath))
 {
  ex.printStackTrace(System.err);
 }}

参考までに: OutOfMemoryError フィードバックは大歓迎です。

Avishek Arang

0
Avishek Arang

開始Javaオプション-Xmxの値を大きくして、たとえば-Xmx512m

0
krosenvold

危険で時間のかかる戦略的なアクションを実行する前に、プログラム内でメモリを大量に消費しているものを正確に確認する必要があります。あなたは答えを知っていると思うかもしれませんが、あなたの目の前に証拠があるまで、あなたは知りません。予期していなかったものがメモリを使用している可能性があります。

プロファイラーを使用します。どちらでも構いません たくさんあります 。まず、各オブジェクトによって使用されているメモリの量を調べます。次に、シリアライザーの反復をステップスルーし、メモリスナップショットを比較して、作成されるオブジェクトまたはデータを確認します。

答えはおそらく、出力をメモリに構築するのではなく、ストリーミングすることです。しかし、最初に証拠を入手してください。

0
Marcus Downing

メモリのサイズを増やすことができますJava -Xmx-optionで使用します。例:

Java -Xmx512M -jar myapp.jar

より良いのは、アプリのメモリフットプリントを減らすことです。何百万ものアイテムをシリアル化しますか?それらすべてをメモリに保持する必要がありますか?または、それらを使用した後にそれらのいくつかを解放できますか?使用するオブジェクトを減らしてみてください。

0
Mnementh

それをうまく処理する実際の方法はありません。それが起こると、あなたは未知の領域にいます。名前でわかります-OutOfMemoryError。そしてそれは次のように説明されています:

Java仮想マシンがオブジェクトのメモリが不足しているためにオブジェクトを割り当てることができず、ガベージコレクタがそれ以上メモリを使用できない場合にスローされます

通常、OutOfMemoryErrorは、システム/アプローチに重大な問題があることを示します(そして、それをトリガーした特定の操作を示すのは困難です)。

多くの場合、それは通常のヒープスペースの不足と関係があります。 -verbosegcを使用し、前述の-XX:+ HeapDumpOnOutOfMemoryErrorが役立つはずです。

問題の簡潔で簡潔な要約は javaperformancetuning にあります。

0
Anonymous