2014年8月30日土曜日

Java GC の動作まとめ


Java GC周りの動きを復習しておく。


Javaのヒープメモリ簡易図

       New            Old(Tenured)        Permanent
 ---------------------------------------------------
|      |   |   |                        |           |
 ---------------------------------------------------
  Eden  Survivor0
             Survivor1


New = Eden + Survivor0 + Survivor1

Survivor領域はそれぞれFrom、To領域とも言われる。
Survivor0と1はGCごとにFromとToが交互に入れ替わる。

Permanent領域にはJVMにロードされるクラスやメソッドの情報が入る。
多数のクラスを動的に生成させたりしなければ、大きく変化しない。



GCの動作概要
GCの種類は大きく2つある。
簡単な例で動きを追ってみる。

● New領域のためのGC(マイナーGC)
1. 新規オブジェクトがEden領域に格納される。

2. Eden領域が一杯になると、New領域でマイナーGCが発生。

3. マイナーGC完了後、生存オブジェクトがSurvivor領域の片方へ移動。

4. 次にマイナーGCが走った場合は、
   Eden領域の新規オブジェクトと、1回目のGCで格納した
   Survivor領域の片方へ移動したオブジェクトが対象となる。
   このGCで生き残ったオブジェクトがSurvivor領域のもう片側へ移動する。

5. 3と4を繰り返し、
   Survivor領域が閾値以上になるか、
   Survivor領域でX回GC実行後も回収されない場合、
   生存しているオブジェクトはSurvivor領域からOld領域へ移る。


● New領域とOld領域を含めたFull GC(メジャーGC)
6. Old領域が設定した閾値になるとすべての領域でFull GCが発動。
   ※Permanent領域が不足してもFull GCが動く。



GCの動作方式
● シリアルGC
GCを1スレッドで実施する。
-----          -----
-----          -----
----- -----  -----
-----          -----
-----          -----
通常処理    GC   通常処理


● パラレル(平行)GC
GCのために複数スレッドを利用できる。
-----         -----
-----  ----  -----
-----  ----  -----
-----  ----  -----
-----         -----
通常処理    GC   通常処理


-XX:+UseParallelGC
パラレルGCを有効

-XX:+UseParallelOldGC
ディフォルトではNew領域のGCのみに対して適用されるが
オプションでFull GCにも適用。

-XX:ParallelGCThreads
各並列実行時のスレッド数の設定



● コンカレント(並列)GC
Full GC時に利用できる以下の方式である。

initial markとremark時にのみ通常処理が停止する。
通常処理
-----          -----           -----
-----          -----           -----
-----          -----           -----
-----          -----           -----
-----          -----           -----
        initial
        mark             remark
                 -----          -----
        -----  -----  -----  -----
                 -----          -----
                concurrent       concurrent
                mark             sweep



-XX:+UseConcMarkSweepGC
Full GCの実行方式をコンカレントGC化

-XX:CMSInitiatingOccupancyFraction
Old領域のGC実行の閾値

-XX:+UseCMSInitiatingOccupancyOnly
CMSの稼働制御
CMSInitiatingOccupancyFraction に達したときだけCMSを稼働

-XX:CMSParallelRemarkEnabled
オブジェクトのremakフェイズをマルチスレッド化

-XX:+UseParNewGC
New領域でパレレルGCを有効
-XX:+UseParallelGC ではないので注意

-XX:ParallelGCThreads
各並列実行時のスレッド数の設定



その他、知っておくべきチューニングキー
-Xms
初期ヒープサイズ

-Xmx
ヒープサイズ全体値

-Xmn(-XX:NewSize)
New領域初期サイズ

-XX:MaxNewSize
New領域の最大サイズ

-XX:SurvivorRatio
Eden領域とSurvivor領域の割合

-XX:TargetSurvivorRatio
Survivor領域がいっぱいと判断される使用率

-XX:NewRatio
New領域とOld領域の割合

-XX:PermSize
Permanent領域最大サイズ

-XX:MaxPermSize
Permanent領域初期サイズ

-XX:SurvivorRatio
New領域内のEden領域とSurvivor領域の設定
ディフォルトは8
Eden : Survivor0 + Survivor1 = 2:8

-XX:TargetSurvivorRatio
Survivor領域の容量の閾値
Survivor領域のX%を超えた場合、
Survivor領域に含まれるオブジェクトをOld領域へ移動させる
大きな値にするとOld領域へ移動させずらくできる

-XX:MaxTenuringThreshOld
Survivor領域からOld領域に移動させるときのGC回数の閾値
X回GCによって生存しているオブジェクトがOld領域へ移動

-XX:+UseThreadPriorities
-XX:ThreadPriorityPolicy
スレッドプライオリティの有効化と、
スレッドのプライオリティの制御

-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintClassHistogram
-XX:+PrintTenuringDistribution
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintPromotionFailure
-XX:PrintFLSStatistics
-Xloggc:/imail/meta/metadata/log/cassmeta_gc.log" 
ログ出力関係

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/crash-`date +%s
メモリ不足時のダンプ出力

-XX:+UseNUMA
NUMA(Non-Uniform Memory Access)を考慮したメモリ配置を実施



統計
GCの動作をモニタリングするために、
jstatの結果から動きをグラフ化しておくとよいだろう。

# jps
12345 HogeDaemon

# jstat -gcutil 12345
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
 25.05   0.00  26.16  45.39  59.82  49475 28076.939  3606 2046.980 30123.919

(見方)
S0     : Survivor space 0 utilization as a percentage of the space's current capacity.  (%)
S1     : Survivor space 1 utilization as a percentage of the space's current capacity.  (%)
E      : Eden space utilization as a percentage of the space's current capacity.  (%)
O      : Old space utilization as a percentage of the space's current capacity.  (%)
P      : Permanent space utilization as a percentage of the space's current capacity.  (times)
YGC    : Number of young generation GC events.  (times)
YGCT   : Young generation garbage collection time.  (ms) 
FGC    : Number of full GC events.  (times)
FGCT   : Full garbage collection time.  (ms) 
GCT    : Total garbage collection time.  (ms) 
 は瞬間値ではなく、積み上げ値(前回からの加算値)である


・速度観点
YGCの回数に対してYGCTのかかった時間が1回3秒以下: YGCT/YGC < 3.0)
FGCの回数に対してFGCTのかかった時間が1回3秒以下: FGCT/FGCT < 3.0)
※差分データがあればもちろんそれでもよい

・容量観点
各領域の使用容量推移: S0、S1、E、O が設計意図通りか



【参考】
● GCがどのように発生しているのかグラフで視認できるツールとして
gclog + GCViewがある。

● JVMの起動オプションに-Xloggc:[file]をつけ、そのログファイルをgcviewerツールで表示するとJVMのメモリ状態を追いやすくなる。

● javaスレッドのスタックトレースを表示させるには、jstackも有用である。

● コードレベルでGCを理解したければ下を強く勧める。
日本ではGC関連の書籍は実質これしかない。
うん?中古しかない?
絶版だが間違いなく良本である。