JavaのOutOfMemoryError発生時の対処について

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 10K+

kidss

score 15

JavaでOutOfMemoryが発生した時の対応策を教えていただきたいです。

処理内容は以下のような形です。コマンドの実行結果は場合によってはとても多くなる事があるのはわかっているのですが、出来ればListに詰めた後で後続処理を実行したいのです。このような場合の対応策としてはどのような方法がありますでしょうか?

JavaVMのチューニングとかは正直調べても中々すぐには理解できずどうするのが正解なのか分かりません。コード上で対応できるのが一番なのですが、、、

どうぞよろしくお願い致します。

処理内容(色々省略してますがこんな感じです)
List<String> testList = new ArrayList<>();
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8"));
br.lines().forEach(line -> {
    testList.add(line);
});

エラー内容
java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3181) ~[na:1.8.0_25]
    at java.util.ArrayList.grow(ArrayList.java:261) ~[na:1.8.0_25]
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235) ~[na:1.8.0_25]
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227) ~[na:1.8.0_25]
    at java.util.ArrayList.add(ArrayList.java:458) ~[na:1.8.0_25]
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+2

出来ればListに詰めた後で後続処理を実行したい

なぜですか? そこに本質的な理由がありますか?

あるのであれば、想定される最大の入力サイズから計算して必要なメモリ量を確保するしかないです。
本質的な理由はなく、気分の問題であるなら、そのような気分でアルゴリズムを選択するのが、技術者としてあるべき姿ではないと思います。
必要に応じて、適切なアルゴリズムを選択できることが、技術者としてあるべき姿であると思います。

なぜ、Listに詰めた後で後続処理を実行したいのか、その理由を説明してください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/04/30 04:15

    > 1行づつ処理できるタイプのデータではなく複数行でひとつのデータとして扱わなければいけないので、ストリーム的に処理できないのです。

    どんな処理なんだろう? たとえば全体を何かのキーでソートするとか? だとしても、何らか方法はあると思います。ストリーム的に処理できないと思い込んでいるだけではないでしょうか。1パスで処理するのではなく、何段階かのパスに分けて段階的に処理する等、適切なアルゴリズムがあると思います。

    > GC overhead limit exceeded のエラーも見られました。

    このエラーは、CPU時間のほとんどがGCに使われている場合に発生します。
    このような現象が起きるというのは、アルゴリズムの選択が不適切である場合がほとんどであると思います。

    > そもそもBufferedReaderで読み込むサイズが大きすぎるのを何とかしないといけないような

    そんなことがあるのかな...そんなことがあるとは思えないんですけどね。

    キャンセル

  • 2015/04/30 17:00

    BufferedReaderの部分は間違ってましたね。調べてみたんですが、一度に全部読み込んでる訳じゃなく、バッファサイズ分読み込んでるので、メモリ消費にはあまり影響がないという考え方であってるでしょうか?

    GC overhead limit exceeded については調べてみると次の2点の場合に発生するということがわかりました。
    ・総時間の98%以上がGCに費やされている
    ・改修されたヒープが2%未満

    そしてGCについては次の通り
    ・参照のないオブジェクトがGCの対象となる
    ・GCのタイミングはJVMが自動で行う(ヒープが残り少ない等)

    以上の点を鑑みて大きいサイズのListでメモリが食い尽くされてるからGCでもメモリが解放されないということでしょうか。
    今ヒープの監視とか解析も調べてますので合わせて確認できたらなと思います。

    アルゴリズムに関しては、おっしゃる通りまだ見直す点はありますので、色々試してみたいと思います。

    キャンセル

  • 2015/05/01 06:12

    巨大なListがあるとき、Listに入っているオブジェクトは当然ながら、GCによって解放されません。GCは、どこからも参照されていないオブジェクトを解放するものですからね。大量のオブジェクトが生成されていて、それがもっぱらListに入っている。そして、ヒープが不足している。ということになりますと、頻繁にGCが発生する一方で、結局解放されるオブジェクトはない、ということになりますので、GC overhead limit exceededになりますね。

    こういう場合は、机上で、メモリを使わないようにするのはどうすれば良いか...を考えるべきです。そもそもListに大量のオブジェクトを入れることは良いことではないんです。たとえばデータベースにデータを格納するとか、中間ファイルを作るとか、巨大なファイルを小さなファイルに分割して処理した結果をマージするとか、いろいろ方法はあります。Listに大量のオブジェクトを入れることがどうして必要なのか。そこが結局は焦点です。

    キャンセル

checkベストアンサー

+1

注意:
※ここから先の内容は、各java開発者の意見が分かれる可能性があります。あくまで一個人の意見としてご覧いただけますと幸いです。僕も日々精進しております。
また参考サイトの情報も合わせて確認の上、他者の見解を仰ぎつつ、oracle公式のガイドドキュメントの確認をするなどしていただくことをお勧めします。

見解:
今回のような事例の場合ですと、まず、addし続けた結果、growが一定回数ごとに起きていますね。
またそれとは別にデータの蓄積が積み重なり、heap領域を超えてしまうこと(ヒープメモリが不足)から発生していると読めそうです。
(表示上は単純に起きるってわかるような感じなので、ここまで単純とは限らないですが)

簡易事例紹介:
DBデータで100万件相当のデータを一度に扱うような場合があり、設計、コード、実行の結果
OutOfMemoryErrorがでたことがあります。
当時は、使用VMメモリ量や、heapサイズの調整、List初期サイズの調整と、
処理中の定期出力を行うなどをして対処をしました。
このときは、緊急性と重要性の確認、扱うデータの特殊性、システムでのデータの扱い、逐次処理であった点を鑑みての判断だったかと思います。

事象が起きた場合と今後:
扱う対象によって、他者もしくは開発アプリ自身の不具合であったり、
サーバー環境の運用上(java系のサーバー利用)で発生する場合であったりすることがあります。

その時々の状況に対してどのような手段をとるかはまちまちであったりするので、
設定の問題なのか、アプリの問題なのか、運用上の問題なのかなど考えることになります。

対策の把握も大事ですが、少なくともjavaVMのメモリ管理の仕組みについてはある程度把握しておくことが肝要ではないでしょうか。

もちろん事象が起きないように設計、定期的なコードレビューをするのは大事ですが、
推測や経験での限界もあるとは思いますので、大量データを扱うなどの規模の大きい場合は
実データを含んだ検証、調査もなるべく早めに合わせて行うことを推奨とします。
最近では、アプリ側の問題が起きないようなコーディングができるようにもなりましたね。
(eclipseではFindBugs とか)
また、事象が起きてしまった場合は、よほどの緊急性、重要性ではない限りは早計な対策の前に、
プロファイルでの事象チェックと原因の切り分け、分析調査は行うようにするなどをしてみてはいかがでしょうか。

参考情報:
参考までに僕も学習させていただいたサイトで情報は古いですが、2つほど紹介します。
1. atmarkitの記事
2. InfoQの記事


ちょっと調べたら事例つきで載せているサイトもありましたので紹介します。


投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/04/28 13:30

    回答ありがとうございます。JavaVMのメモリ管理については勉強しなければいけないと思っていましたので、少しづつでも理解していきたいです。参考のリンク先も大変勉強になりそうです。

    キャンセル

+1

こんな工夫をしている例を見つけました。
- Javaで大量データをメモリに展開するテクニックの考察 http://symfoware.blog68.fc2.com/blog-entry-1012.html
...
パターン    Listに入った個数
通常 476
シリアライズ 4616
シリアライズ + zip    16573
...

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/04/30 16:29

    arguisさんのデータ圧縮案で調べてたらこのページを見つけました。これだったら大分メモリ消費変わりますよね。ただ、パフォーマンスの部分も気になるので一度計測して確認してみたいと思います。ありがとうございます。

    キャンセル

0

すべてのデータをオンメモリーにしたいのなら、ヒープの最大サイズを大きくするしかないですね。
要らないデータを削除するのであればチューニングの余地はあるかもしれません。


別の解法としては、おすすめはしませんが、データ圧縮を使う手もあります。
もちろんパフォーマンスは大幅に低下しますし、どのくらい削減できるかは分かりません。
このケースだと1行ずつ圧縮しなければ使い物にならなそうですしね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/04/28 13:37

    なるほど、、、データ圧縮は知りませんでした。確かにパフォーマンスを考慮すると実際に用いるのは難しいかもしれませんが、ヒープサイズを大きくする事と合わせて考慮します。回答ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る