■環境
・JDK 6、CentOS 5
■アプリの内容
・TCP/IPでリクエストを受け付ける独自実装のサーバです。
・SpringとHibernateを使用しています。
・hot reloadingは行っていません。
■やったこと
・jmap -dump →PermGen領域はヒープではないので確認不可だった
・jmap -permstat →クラスローダごとの使用量しかわからなかった
・jmap -heap →現在の使用量合計しかわからなかった
・-verbose オプションを付けてアプリケーションを起動。1秒おきに jmap -heap して PermGen 使用量を確認 →クラス読み込みとは関係のなさそうなタイミングで PermGen 使用量が増えていた
最初は「単純に上限を増やせばいいか」と思ったのですが、再発させたくないので、今のうちに原因を突き止めたいと思っています。
PermGen領域の内訳を直接確認する方法があれば、一発で解決しそうなんですが……。
処理を削除していくしかないです。
処理を削除してどれぐらい 使用量が減ったかで 確認します。
あとは こちらも参考に
http://www.itmedia.co.jp/enterprise/articles/0907/10/news002.html
1秒おきに jmap -heap して PermGen 使用量を確認 →クラス読み込みとは関係のなさそうなタイミングで PermGen 使用量が増えていた
何をもって、クラスのロードとは関係ない、としたのでしょうか?
以下のオプションを VM の起動オプションに設定して、状況を確認してみましょう。
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 領域を大量に使うかも、というような根拠があったりするのでしょうか?
回答ありがとうございます!
何をもって、クラスのロードとは関係ない、としたのでしょうか?
"-verbose" オプションを付けて起動するとクラスのロード/アンロードが出力されますよね
(-XX:+TraceClassLoading -XX:+TraceClassUnloading で出力されるものと同じ?)。
そのタイミングと、PermGen領域のサイズが増えるタイミングが一致しなかったので、
「関係なさそう」と判断しました。
(追記: クラスやメソッドのデータがPermGen領域を多く占めているのは確かですが、
再現するための起動しっぱなしにしたときに、クラスロードとは別のタイミングで
PermGenが増えていたので、別の要因もあるのではと思った、という意味です)
"-XX:+PrintGCDetails" はまだ試していませんでした。やってみます!
「何が領域を大量に使ってるか」というのは、
何か Perm 領域を大量に使うかも、というような根拠があったりするのでしょうか?
ごめんなさい、書き方が悪かったです。
「何がどれだけ領域を使っているか」を知りたいと思っています。
最終的には、「クラスファイルの合計がこれだけであって、動的に作られるクラスが
これくらいになるから、PermGen領域は256MBあれば確実に大丈夫だぜ!」
みたいなことが言えたらなー、と思っているのですが……。
Perm 領域は、じわじわと増えていくはず
これって、どういう意味合いでしょう? 僕が知っているのは
くらいなのですが、他にも何かあったらぜひ教えてください。
もしかして、どこまでもじわじわ増えていくのが当たり前だったりしますか?
だとすると、「PermGen使用量ってのは、いつかは上限を超えちゃうもの」……?
Perm 領域は、じわじわと増えていくはず
これって、どういう意味合いでしょう? 僕が知っているのは
「クラスが少しずつ (必要になったものから) 読み込まれていくから」のつもりで書きました。
常駐型のプログラムのようなので、一通り処理が動いてしまえば、
必要なクラスのほとんどはロードされてるだろうな、という想定です。
だとすると、「PermGen使用量ってのは、いつかは上限を超えちゃうもの」……?
それは無いです。
Full GC で、Perm 領域は回収されます。
もちろん、回収されるための条件を満たしている必要はあります。
これくらいになるから、PermGen領域は256MBあれば確実に大丈夫だぜ!」
id:sardine さんって、実は、開発歴が長くて、メモリをきちんと見積もる時代からやってた世代なんでしょうか?
ぼくは、java が世に出る前からやってるので、バイト単位で使用するリソースを決める、というのが当たり前の世代です。
ですが、java の場合は、heap も含めてメモリサイズはチューニングするものだと割り切ってます。
もちろん、開発前の目標というか、目安はもちろん決めますが、数十バイト単位で
カッチカッチに調整する、というよりは、運用環境や動作状況に応じて、
安全な側に倒した値にチューニングしていくもんだ、と。
実際、測定するにしても、inner クラスがあったり、無名クラスがあったりと、
ひとつひとつのサイズを決めていくのは、気が遠くなります。
また、JDK も含めたライブラリのバージョンが上がることによって、サイズも微妙に変わるでしょうし。
もちろん、リークしてるとやばいので、それが無いことを確認する意味で、
perm を多めにしておいて、しばらく動作させっぱなしにして、perm と heap の
サイズを監視することを奨めます。
最悪な場合を想定すると、perm を増やしても、動作してる時間が長いと OutOfMemoryError が出てしまう、という可能性もありますので。
再度の回答ありがとうございます!
java の場合は、heap も含めてメモリサイズはチューニングするもの
まったくおっしゃる通りだと思います。「確実に大丈夫」を求めても不毛ですね……。
僕自身は、これまで書いたプログラムのほとんどがJavaです(子供のころはBASICでしたけど)。
ですので、むしろ「バイト単位で使用するリソースを決める」スキルがないくらいです。
ただ、今回OutOfMemoryErrorが出たのを受けて、上限をいくつに引き上げるか検討する中で
「もうちょっと材料がほしいな」と思ったんです。
ネットで調べたり周囲の人に聞いてみたりしたところ、ヒープやPermGen領域のサイズは
「実測値に、安全のため何割かプラスする」というのが多かったです。
この「何割」というのを決めるにしても、今回エラーが起きた原因が何なのかがあった方が
より現実味のある値が出せるんじゃないかなぁと。
などなど……。
……と書いてみましたが、それがわかってもあんまり現実味は増さないのかな。
皆さんどうやって決めてるんでしょうね。
「たぶん30%も足しておけば大丈夫だろ! エイヤ!」みたいなノリなんでしょうか。
回答ありがとうございます!
# なぜ回答者さんが全員動物アイコンなのか……。
MATってPermGen領域は解析できなくないですか?
回答ありがとうございます!
……が、微妙に違ったみたいです。
「MaxPerm サイズを 256m に変更」してねと書いてあるけど、
その 256 っていう数字はどうやって決めたんだろうっていう話なのです。
「たぶん30%も足しておけば大丈夫だろ! エイヤ!」みたいなノリなんでしょうか。
僕が決めるときの基準は、「残り」です。
そのプログラムが動いているサーバで、空いてるメモリがあるなら、50%~100% 増やしちゃいます。
逆に、使えるメモリがあまりないなら、コードの方のチューニングにとりかかります。
とはいえ、クラスの数を気にして、設計が歪んでしまうのは本末転倒なので、
というような感じで改善していくかな。
以下、URL 必須なので、手持ちの URL から無理やり探しました (^^;
重ねての回答ありがとうございます!
ID は a-kuma3 なのに回答は天使さんのようですね!
決めるときの基準は、「残り」
使えるメモリがあまりないなら (略) 設計が歪んでしまうのは本末転倒なので (略) heap を減らして、perm を増やす
恥ずかしながら「ヒープの余裕ある分を回す」という発想がなかったので
ハッとさせられました。
全体的に、冷静に見なおしてみたいと思います。
URL 必須なので、手持ちの URL から無理やり探しました (^^;
お手数をおかけしてすみません。No.3 の回答で一見 URL がなかったので
「あれ? 2 回目だとなくてもいけるのかな?」とか思ってたんですが、
実はうまく工夫して頂いてたんですね。
リンク先も参考になりました。ありがとうございます
(id:a-kuma3 さん執筆の記事だったりするのでしょうか?)。
回答ありがとうございます!
実測して切り分けあるのみ、ですか……。
他の部分で同じエラーが再発するを避けるには、
「全パターンの処理を流して、全部のクラスが読み込まれた状態でサイズを測ってみるしかない」
ということになるのでしょうか。
事前に「これだけあれば確実に大丈夫」という数字がわかれば、かなり安心なのですが……。