#最近お仕事でシステムのリソース状況を見る機会が多いため、覚書として書いておきたいと思います。
まず、基本事項ですがJavaでプログラムを作成すると、C言語などとは違ってメモリを自動で管理してくれます。例えば、プログラム内で変数を宣言して正しく開放されない変数が残っていたりすると、次第にメモリ利用上不都合な状態(これをフラグメンテーション、或いは断片化と言います)になってしまいます。本来であればすべての変数に対して開放する処理を記述してあげる必要があるのですが、Javaではそういった面倒な記述が不要で、Java自身が自動で管理してくれます。具体的には、JavaVM内で利用しているメモリを自動整理する、という仕組み(ガーベジコレクション:GCと言います)が動きます。
もう少し詳しく見ていきます。
JavaVMでは、メモリ領域を下記図のように種別して管理します。
大きく分けると、
・New領域
→生存期間の短いJavaオブジェクトが格納される
・Old領域
→生存期間の長いJavaオブジェクトが格納される
・Permanent領域
→ロードされたclassの情報が格納される
の3つになります。
New、Old領域の2つをJavaヒープ領域といいます。Javaヒープ領域では、オブジェクトを世代別に管理する仕組みになっており、新規に生成されたJavaオブジェクトはまずNew領域内のEden領域に格納されます。その後Edenがいっぱいになると、EdenのGC(YoungGC:YGCといいます)が発生し、まだ利用中であるオブジェクトのみをSurvivor領域の"To"に移動させ、利用していないオブジェクトは開放します。次のYGC時にはSurvivor領域のFromとToが入れ替わり、Eden領域のオブジェクトを"元々From:現To"に移し、同時に"元々To:現From"に格納されていたオブジェクトで利用中のものを"元々From:現To"の領域に移します。
さらにYGCを一定回数繰り返してもなおSurvivor領域に残っているオブジェクトが「生存期間の長いオブジェクト」だと判断され、Old領域に移されます。Old領域がいっぱいになってしまうと、FullGCと呼ばれるGCが発生し、JVMで稼働している全オブジェクトが停止してしまいます。。。(俗に、"Stop the World"といいます。なんかかっこいいw)なので、一般的にはFullGCが発生しないように、適切なチューニングを行います。
上記で記載した各領域のサイズは、Java実行時にオプションとして定義します。指定オプションを下記で紹介します。
- -Xms
- ヒープ初期サイズを指定
- 例:-Xms2048k、-Xms2m
- -Xmx
- ヒープ領域最大サイズを指定
- 例:-Xmx2048k、-Xmx2m
- -XX:NewSize
- New領域初期サイズを指定
- -XX:MaxNewSize
- New領域最大サイズを指定(初期サイズと同じがベター)
- -XX:Permsize
- Permanent領域初期サイズを指定
- -XX:MaxPermsize
- Permanent領域最大サイズを指定
Javaを利用しているWebアプリケーションでは、FullGC発生対策として一般に下記が考えられるのかなと思います。
・Javaアプリケーションサーバの定期再起動(一日1回とか)
→サービス停止が必要
・割当メモリの増設
→FullGC発生頻度は減るが、発生してしまった時の停止時間が増えてしまう。
"→"で記載したように、デメリットもあります。
勿論、そもそものアプリケーションの作りが悪いから改修する、という方法もありますが現場だと中々そっちの手段は取れないのかな、、、と思います。
ただ、最近では「コンカレントGC」というFullGCで停止してしまう時間を最小化する仕組みもあり、Javaも進化してきておりますw
次回はPermanent領域について触れます。
0 件のコメント:
コメントを投稿