※この投稿は、丸太式 Advent Calendar 2014の9日目です
今日はJJUG CCC 2014 Fallの参加レポート3本目です。
概要
R1-6 Concurrent Mark-Sweep Garbage Collection 再入門
by 久保田 祐史さん (@sugarlife)
メモ
CMS GC
- GC: 不要なメモリ領域を探し、解放して再利用可能とする
- Parallel GC: アプリケーションを止めて動く。GC時間は短いが、応答しなくなる
- Concurrent GC: アプリケーションと同時に動く
- Concurrent Mark-Sweep GC: ゴミじゃない領域をMarkし、Markされていない領域をSweepして再利用可能にする
Compactionしないので、メモリ空間が断片化する
HotSpot VMのCMS GC
- Heap -> New Gen.(Eden, Survivor0, Survivor1), Old Gen.(Tenured)
- Minor GC: New Gen.に対してParallel Copy GC: アプリケーションを止めて、メモリが断片化しないようにGC
- Major GC: Old Gen.に対してCMS GC
Minor GC: Eden -> Survivor 0, Survivor 0 -> Survivor 1
- Promotion : Survivor -> Tenured
- Major GC: Old領域の占有率やHeap使用量増加速度によって発生する
CMS GCは安定していない。失敗してSTW:Stop The Worldを伴うFull GCが発生することがある
Full GC (with STW): 全領域がGC対象
Promotion failed, Concurrent mark failed
GCログの読み方
- タイムスタンプ: Java processのuptime (sec.)
-XX:+PrintGCDateStampsで、読める時間表記で出力 - Minor GC: ParNew GC前のNew領域サイズ->GC後のNew領域サイズ(総容量)
かかった時間は後ろの方に出るtimes (real)を参照 Minor GC (promotion): PromoteされたサイズはNew領域の差分 - 全体の領域の差分
CMS GC: initial mark
STWをかけて、ルートオブジェクトから直接たどれるオブジェクトをマーキング- concurrent mark
アプリケーションを再開しつつ、initial markでマーキングされたオブジェクトからたどれる全オブジェクトをマーキング。
処理中に生成・変更されたオブジェクトはDirty Cardにされる - cuncurrent preclean
STWが発生するRemarkを短縮するため、concurrent mark中にDirty Cardのオブジェクトをマーキング - concurrent abortable preclean
Minor GCによってEden使用量が2MBになるまで最大5秒待つ - Remark (Final mark)
STWしてNew領域を全スキャンして、New領域からOld領域を参照しているものにmark - concurrent sweep
未マークのオブジェクトを改修 - concurrent reset
次のCMS GCで使う関連マップをクリアして、initial markの処理にゴミを残さない
注意すべきパターン
- Full GC: promotion failed: Minor GCでOld領域にPromoteできなかった場合に発生
断片化を解消するためのFull GC- 短命オブジェクトはNew領域で改修するべき(断片化を防ぐ)
- New領域のサイズを調整する
- PromoteさせずにNew領域で粘る
- 兆候: Minor GC後のOld領域より、次のMinor GCの前のOld領域が大きい->短命オブジェクトがOld領域に飛んでる
- 兆候: Old > NewだがPromotion Failed: Newに大きいオブジェクトしか入っていない
Full GC: concurrent mode failure: CMS GC中にもう一度CMS GCが発生しようとした場合
- CMS GCの改修が間に合っていないので、開始タイミングを早める
- サーバのスペックをあげる
- 単純にいメモリを増やすより、CPUスペックをあげるべき
GC locker: GCを止める機構。HotSpot初期化や、JNI API呼び出しの一部
- GC locker: Trying a full collection because scavenge failed
- GCが止まっているのでNew領域にはいらずOLD領域に割り当て
- 深刻な断片化、Promote失敗
- 対策
- GC Lockerの頻度を下げる
- 断片化しないGCを使う
- 短命JVM