Javaで作成した常駐型アプリケーションの実行中に "java.lang.OutOfMemoryError: PermGen space" が発生したのですが、原因 (何が領域を大量に使ってるか) を特定するにはどんな方法がありますか?


■環境
・JDK 6、CentOS 5

■アプリの内容
・TCP/IPでリクエストを受け付ける独自実装のサーバです。
・SpringとHibernateを使用しています。
・hot reloadingは行っていません。

■やったこと
・jmap -dump →PermGen領域はヒープではないので確認不可だった
・jmap -permstat →クラスローダごとの使用量しかわからなかった
・jmap -heap →現在の使用量合計しかわからなかった
・-verbose オプションを付けてアプリケーションを起動。1秒おきに jmap -heap して PermGen 使用量を確認 →クラス読み込みとは関係のなさそうなタイミングで PermGen 使用量が増えていた

最初は「単純に上限を増やせばいいか」と思ったのですが、再発させたくないので、今のうちに原因を突き止めたいと思っています。
PermGen領域の内訳を直接確認する方法があれば、一発で解決しそうなんですが……。

回答の条件
  • 1人10回まで
  • 13歳以上
  • 登録:2011/08/09 16:06:56
  • 終了:2011/08/15 22:42:38

回答(6件)

id:taknt No.1

きゃづみぃ回答回数13537ベストアンサー獲得回数11982011/08/09 16:10:13

ポイント300pt

処理を削除していくしかないです。

処理を削除してどれぐらい 使用量が減ったかで 確認します。

あとは こちらも参考に

http://www.itmedia.co.jp/enterprise/articles/0907/10/news002.html

id:sardine

回答ありがとうございます!

実測して切り分けあるのみ、ですか……。

他の部分で同じエラーが再発するを避けるには、

「全パターンの処理を流して、全部のクラスが読み込まれた状態でサイズを測ってみるしかない」

ということになるのでしょうか。

事前に「これだけあれば確実に大丈夫」という数字がわかれば、かなり安心なのですが……。

2011/08/09 17:45:32
id:a-kuma3 No.2

a-kuma3回答回数4441ベストアンサー獲得回数18252011/08/09 17:17:07

ポイント300pt

1秒おきに jmap -heap して PermGen 使用量を確認 →クラス読み込みとは関係のなさそうなタイミングで PermGen 使用量が増えていた

何をもって、クラスのロードとは関係ない、としたのでしょうか?


以下のオプションを VM の起動オプションに設定して、状況を確認してみましょう。

  • XX:+TraceClassloading
  • XX:+TraceClassUnloading
  • XX:+PrintGCDetails

http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

http://java.sun.com/docs/hotspot/gc1.4.2/faq.html


Perm 領域は、じわじわと増えていくはずなので、何かひとつが原因で大量に消費する、

というものではないと思うのですが、「何が領域を大量に使ってるか」というのは、

何か Perm 領域を大量に使うかも、というような根拠があったりするのでしょうか?

id:sardine

回答ありがとうございます!

何をもって、クラスのロードとは関係ない、としたのでしょうか?

"-verbose" オプションを付けて起動するとクラスのロード/アンロードが出力されますよね

(-XX:+TraceClassLoading -XX:+TraceClassUnloading で出力されるものと同じ?)。

そのタイミングと、PermGen領域のサイズが増えるタイミングが一致しなかったので、

「関係なさそう」と判断しました。

(追記: クラスやメソッドのデータがPermGen領域を多く占めているのは確かですが、

再現するための起動しっぱなしにしたときに、クラスロードとは別のタイミングで

PermGenが増えていたので、別の要因もあるのではと思った、という意味です)

"-XX:+PrintGCDetails" はまだ試していませんでした。やってみます!

「何が領域を大量に使ってるか」というのは、

何か Perm 領域を大量に使うかも、というような根拠があったりするのでしょうか?

ごめんなさい、書き方が悪かったです。

「何がどれだけ領域を使っているか」を知りたいと思っています。

最終的には、「クラスファイルの合計がこれだけであって、動的に作られるクラスが

これくらいになるから、PermGen領域は256MBあれば確実に大丈夫だぜ!」

みたいなことが言えたらなー、と思っているのですが……。

Perm 領域は、じわじわと増えていくはず

これって、どういう意味合いでしょう? 僕が知っているのは

  • クラスが少しずつ (必要になったものから) 読み込まれていくから
  • リフレクションを高速化するために、HotSpotがクラスを動的生成するから

くらいなのですが、他にも何かあったらぜひ教えてください。

もしかして、どこまでもじわじわ増えていくのが当たり前だったりしますか?

だとすると、「PermGen使用量ってのは、いつかは上限を超えちゃうもの」……?

2011/08/09 18:34:26
id:a-kuma3 No.3

a-kuma3回答回数4441ベストアンサー獲得回数18252011/08/09 18:48:09

ポイント300pt

Perm 領域は、じわじわと増えていくはず

これって、どういう意味合いでしょう? 僕が知っているのは

「クラスが少しずつ (必要になったものから) 読み込まれていくから」のつもりで書きました。

常駐型のプログラムのようなので、一通り処理が動いてしまえば、

必要なクラスのほとんどはロードされてるだろうな、という想定です。


だとすると、「PermGen使用量ってのは、いつかは上限を超えちゃうもの」……?

それは無いです。

Full GC で、Perm 領域は回収されます。

もちろん、回収されるための条件を満たしている必要はあります。


これくらいになるから、PermGen領域は256MBあれば確実に大丈夫だぜ!」

id:sardine さんって、実は、開発歴が長くて、メモリをきちんと見積もる時代からやってた世代なんでしょうか?

ぼくは、java が世に出る前からやってるので、バイト単位で使用するリソースを決める、というのが当たり前の世代です。

ですが、java の場合は、heap も含めてメモリサイズはチューニングするものだと割り切ってます。

もちろん、開発前の目標というか、目安はもちろん決めますが、数十バイト単位で

カッチカッチに調整する、というよりは、運用環境や動作状況に応じて、

安全な側に倒した値にチューニングしていくもんだ、と。


実際、測定するにしても、inner クラスがあったり、無名クラスがあったりと、

ひとつひとつのサイズを決めていくのは、気が遠くなります。

また、JDK も含めたライブラリのバージョンが上がることによって、サイズも微妙に変わるでしょうし。


もちろん、リークしてるとやばいので、それが無いことを確認する意味で、

perm を多めにしておいて、しばらく動作させっぱなしにして、perm と heap の

サイズを監視することを奨めます。

最悪な場合を想定すると、perm を増やしても、動作してる時間が長いと OutOfMemoryError が出てしまう、という可能性もありますので。

id:sardine

再度の回答ありがとうございます!

java の場合は、heap も含めてメモリサイズはチューニングするもの

まったくおっしゃる通りだと思います。「確実に大丈夫」を求めても不毛ですね……。

僕自身は、これまで書いたプログラムのほとんどがJavaです(子供のころはBASICでしたけど)。

ですので、むしろ「バイト単位で使用するリソースを決める」スキルがないくらいです。

ただ、今回OutOfMemoryErrorが出たのを受けて、上限をいくつに引き上げるか検討する中で

「もうちょっと材料がほしいな」と思ったんです。

ネットで調べたり周囲の人に聞いてみたりしたところ、ヒープやPermGen領域のサイズは

「実測値に、安全のため何割かプラスする」というのが多かったです。

この「何割」というのを決めるにしても、今回エラーが起きた原因が何なのかがあった方が

より現実味のある値が出せるんじゃないかなぁと。

  • 思っていたよりもクラスが多く使われたのか?
  • 動的生成されるクラスの数やサイズが思っていたより大きかったのか?
  • HotSpotが最適化に使用する内部データの量が結構大きいのか?

などなど……。

……と書いてみましたが、それがわかってもあんまり現実味は増さないのかな。

皆さんどうやって決めてるんでしょうね。

「たぶん30%も足しておけば大丈夫だろ! エイヤ!」みたいなノリなんでしょうか。

2011/08/09 20:43:48
id:km1981 No.4

km1981回答回数429ベストアンサー獲得回数492011/08/09 20:42:14

ポイント10pt

開発環境でEclipseを使っているならMATが定番です

http://www.eclipse.org/mat/

id:sardine

回答ありがとうございます!

# なぜ回答者さんが全員動物アイコンなのか……。

MATってPermGen領域は解析できなくないですか?

2011/08/09 20:53:23
id:mirakurutoshiki No.5

mirakurutoshiki回答回数157ベストアンサー獲得回数32011/08/09 22:30:31

ポイント10pt

これですか?

http://help.adobe.com/ja_JP/livecycle/es/releasenotes.html

まちがってたらすいません

id:sardine

回答ありがとうございます!

……が、微妙に違ったみたいです。

「MaxPerm サイズを 256m に変更」してねと書いてあるけど、

その 256 っていう数字はどうやって決めたんだろうっていう話なのです。

2011/08/10 00:25:09
id:a-kuma3 No.6

a-kuma3回答回数4441ベストアンサー獲得回数18252011/08/09 22:39:52

ポイント300pt

「たぶん30%も足しておけば大丈夫だろ! エイヤ!」みたいなノリなんでしょうか。

僕が決めるときの基準は、「残り」です。

そのプログラムが動いているサーバで、空いてるメモリがあるなら、50%~100% 増やしちゃいます。


逆に、使えるメモリがあまりないなら、コードの方のチューニングにとりかかります。

とはいえ、クラスの数を気にして、設計が歪んでしまうのは本末転倒なので、

  1. コピペで作られてるようなクラスが無いかどうかを確認
  2. heap の使用状況を確認
  3. 無駄に heap を使ってるようなら、コードの見直し
  4. heap を減らして、perm を増やす

というような感じで改善していくかな。


以下、URL 必須なので、手持ちの URL から無理やり探しました (^^;

http://www.whitemark.co.jp/tec/java/javagc.html

id:sardine

重ねての回答ありがとうございます!

ID は a-kuma3 なのに回答は天使さんのようですね!

決めるときの基準は、「残り」

使えるメモリがあまりないなら (略) 設計が歪んでしまうのは本末転倒なので (略) heap を減らして、perm を増やす

恥ずかしながら「ヒープの余裕ある分を回す」という発想がなかったので

ハッとさせられました。

全体的に、冷静に見なおしてみたいと思います。

URL 必須なので、手持ちの URL から無理やり探しました (^^;

お手数をおかけしてすみません。No.3 の回答で一見 URL がなかったので

「あれ? 2 回目だとなくてもいけるのかな?」とか思ってたんですが、

実はうまく工夫して頂いてたんですね。

リンク先も参考になりました。ありがとうございます

(id:a-kuma3 さん執筆の記事だったりするのでしょうか?)。

2011/08/10 00:54:40
  • id:a-kuma3
    >リンク先も参考になりました。ありがとうございます
    >(id:a-kuma3 さん執筆の記事だったりするのでしょうか?)。
    全然違いますよ :-)
    ぼくも、いろいろ悩んだ時期があって、そのときに調べたときに当たったページです。
    内容的には、若干古いです。
    今どきの heap のチューニングでは、Eden とか new の領域でも、
    どう使ってるか、というのまで意識した情報の方が多いんじゃないか、と。

    また、チューニングにおいては、VM の実装がどこのものか、ってのも結構重要なので、
    先のリンクページについては、話半分程度で読んでください。
  • id:a-kuma3
    >思っていたよりもクラスが多く使われたのか?
    蛇足ですが、それに類することで、最近のプロジェクトでの経験を二つほど。

    ・JSP のクラス
    ユーザごとのコンテンツも JSP で作ってもらったりしてたので、
    出荷時の perm サイズの推奨値がギリギリすぎて、ユーザによっては、perm サイズが足りなくなってしまいました。
    出荷時の検証では、全く検出できなくて、あほだなあ(→自分)、と思いました。

    ・Log4j が heap をリークする?
    社内の情報で、Log4j が heap を肥大化させる、なんてのがまわってて。
    カテゴリーごとにインスタンスを保持することを指してたみたいなんですけど、
    そんなの事前に検証してれば、一発で分かるじゃん、みたいな。
    実際にプログラムを回して、数値をとって検証してみるのは大事だよね、って言う話。
  • id:sardine
    コメントありがとうございます!
    おかげさまで、「だいたいこのくらいでいけそう」という数字は見えてきました。

    VM の実装が変わればチューニングも変わる。確かにその通りですね。バージョンでの違いも含めて。
    Java 8 では JRockit と HotSpot が統合されるらしいですから、PermGen も領域自体なくなるのかな?

    体験談も参考になりました。
    JSP も、実行するまでサイズがわからない類ですね。今回問題になっているアプリでは JSP を
    使っていませんが、今後のために覚えておきます。
    Log4j は、今回ネットでいろいろ調べている中で、何回か遭遇しました。特に Web アプリ関連で
    undeploy してもヒープが解放されないとか、例外が出るとか。これも、今回は関係なかったですが、
    そのうち出くわしそうな気がします。

    >実際にプログラムを回して、数値をとって検証してみるのは大事だよね、って言う話。

    身に沁みます……。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません