超先取りShenandoahGC

352 views

Published on

JJUG CCC 2016 fall のセッションスライドです。
アップロード用に追記あり。

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
352
On SlideShare
0
From Embeds
0
Number of Embeds
101
Actions
Shares
0
Downloads
7
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

超先取りShenandoahGC

  1. 1. 超先取り ShenandoahGC Yohei Oda @JJUG CCC 2016 Fall
  2. 2. 今日のトピック 絶賛開発中 http://openjdk.java.net/projects/shenandoah/
  3. 3. CMS,G1,Shenandoah 回収アルゴリズム 並行性 コンパクション CMS スイープ ◯ ☓(断片化放置) G1 コピー ☓(STW) ◯ Shenandoah コピー ◯ ◯
  4. 4. やりたいこと あるリージョンの生存オブジェ クトだけを別のリージョンに移 動(退避)して元の領域を解放 したい ただし退避はアプリケーション を止めずに行う
  5. 5. 一般的な退避 A’ B C A 1.退避させたいオブジェクトAのコピーA’を空き リージョンに作る 2.Aへの参照をA’に向けるようにする 3.これをリージョン内の全生存オブジェクトに対して 行ったら、リージョンを解放できる Javaスレッド 退避対象リージョン 空きリージョン 退避対象外リージョン
  6. 6. 並行退避の問題 A B C A’ Aを参照するオブジェクトは複数ある(B,C)ため、 BとCがA’を参照するようになるタイミングにはラ グができる。 このタイミングでオブジェクトが更新されると、 同じオブジェクトであるはずのAとA'のデータが ずれてしまい、参照経路によって取り出されるデ ータが変わってしまう。 = ? 参照/更新されるのがAかA’か不定なのはダメ B経由、C経由のどちらの経路でもA'にアクセスできる仕組みが必要
  7. 7. BrooksPointer(間接参照) Object pointer field field • オブジェクトごとに一つポインタ(BrooksPointer)をセットで作成する • 普段はBrooksPointerは同時に作られたオブジェクトへのポインタになっている • オブジェクトのフィールドには参照するオブジェクトのBrooksPointer のアドレスが入る
  8. 8. BrooksPointerを使った退避 A CB A’ AのBrooksPointerをA'に向けておくと、 B or C → A の参照が更新されていない状態でも、 BrooksPointerを経由して A’ にアクセスさせる ことができる
  9. 9. BrooksPointer切り替え時の問題 A CB A’を作ってからBrooksPointerをA'に切り替えるまでの間に Aが更新された場合、その更新内容は消えてしまう 退避処理と更新処理が独立して行われているのが問題 これらの処理の前後関係を規定する必要がある A CB A’ A CB A’
  10. 10. ライトバリア または A A’A Aに対して更新処理が行われると、アプリケーションは更新処理の前に Aの退避(コピーとBrooksPointerの切り替え)を行うようにする。 のとき 1 0 1 0例えば、 のとき「0を1に変更したい」のであれば である。 なお、JavaスレッドやGCスレッドのBrooksPointer切り替えが競合しないよう、 BrooksPointerの更新は CAS を使って行う。 Aが退避対象のリージョンにあり、BrooksPointer切り替え前、つまり
  11. 11. CAS コンペア・アンド・スワップ(Compare-and-Swap、CAS)とは、アトミックに、あるメモリ位置の内容と指定された値 を比較し、等しければそのメモリ位置に別の指定された値を格納するCPUの特別な命令の一種である。この操作の結果、 置換が行われたかどうかを示す必要があり、単純な真理値を返すか、そのメモリ位置から読み込んだ内容(書き込んだ内 容ではない)を返す。(中略) CAS命令を利用したアルゴリズムは、一般にあるキーとなるメモリ位置を読み取り、その古い値を記憶しておく。その古 い値に基づいて、新しい値を計算する。その後、CAS命令でそのメモリ位置に新しい値を格納するが、そのときにCAS命 令の比較によって計算に用いた古い値が置換時にもそのまま入っていることを確認する。CAS命令が比較に失敗した場合 、最初から処理をやり直す。メモリ位置を再度読み取って、値を計算し、CAS命令を再実行するのである。 https://ja.wikipedia.org/wiki/コンペア・アンド・スワップ あるスレッドがBrooksPointerの値を変更した後で別のスレッド が意図せずBrooksPointerの切り替えようとした場合、書き換え を防ぐことができる = 一つのBrooksPointerが2度更新されることを防げる
  12. 12. BrookPointer+ライトバリア+CAS ① ② ③ ④ Java スレッド 0 0 0 0 0 0 0 0 0 0 0 0 +1したい GCスレッドが先に BrooksPointerを切り替 え JavaスレッドもBrooksPointerを切 り替えようとするが、 BrooksPointerが変更されているた めCASが失敗する そのままBrooksPointerの参 照を辿って更新処理をすれ ばOK まだBrooksPointerは切り 替えられていないので Javaスレッドはコピーを 作成する 1
  13. 13. BrookPointer+ライトバリア+CAS(逆) ① ② ③ ④ Java スレッド 0 0 0 0 0 0 0 1 0 0 1 0 +1したい Javaスレッドが先に BrooksPointerを切り替 え Javaスレッドがオブジェクトを 更新 GCスレッドが BrooksPointerを切り替えよ うとしてもCASで失敗する まだBrooksPointerは切り 替えられていないので Javaスレッドはコピーを 作成する
  14. 14. ShenandoahGCのログ Pause Init-Mark (2.346s, 2.347s) 0.274ms Concurrent marking 12M->12M(16M) (2.347s, 2.348s) 1.590ms Pause Final Mark 12M->4M(16M) (2.348s, 2.351s) 2.599ms Concurrent evacuation 4M->5M(16M) (2.351s, 2.351s) 0.155ms
  15. 15. ShenandoahGCの流れ Pause Init-Mark (2.346s, 2.347s) 0.274ms Concurrent marking 12M->12M(16M) (2.347s, 2.348s) 1.590ms A CB 全ての生存オブジェクトをマークしていく
  16. 16. ShenandoahGCの流れ Pause Init-Mark (2.346s, 2.347s) 0.274ms Concurrent marking 12M->12M(16M) (2.347s, 2.348s) 1.590ms Pause Final Mark 12M->4M(16M) (2.348s, 2.351s) 2.599ms A CB ConcurrentMark中に変更されたヒープの状 態を反映して、生存オブジェクトが確定する 。 確定したら、ごみの量に基いてコレクション セット(退避対象となるリージョンの集合) が決定される
  17. 17. ShenandoahGCの流れ Pause Init-Mark (2.346s, 2.347s) 0.274ms Concurrent marking 12M->12M(16M) (2.347s, 2.348s) 1.590ms Pause Final Mark 12M->4M(16M) (2.348s, 2.351s) 2.599ms Concurrent evacuation 4M->5M(16M) (2.351s, 2.351s) 0.155ms A CB A’ コレクションセット内の生存オブジェクトを 空きリージョンに退避させる。 GCスレッドが退避させる前にアプリケーショ ンによる更新が行われた場合は、Javaスレッ ドが退避を実行する。
  18. 18. ShenandoahGCの流れ A CB A’ Pause Init-Mark (2.346s, 2.347s) 0.274ms Concurrent marking 12M->12M(16M) (2.347s, 2.348s) 1.590ms Pause Final Mark 12M->4M(16M) (2.348s, 2.351s) 2.599ms Concurrent evacuation 4M->5M(16M) (2.351s, 2.351s) 0.155ms Pause Init-Mark (2.926s, 2.928s) 1.869ms Concurrent marking 12M->12M(16M) (2.928s, 2.932s) 3.416ms 次のGCのマークフェイズで、辿っている参照 がコレクションセット内のオブジェクトを指し ていたら、マークついでに退避先に参照を切り 替える。
  19. 19. ShenandoahGCの流れ A CB A’ Pause Init-Mark (2.346s, 2.347s) 0.274ms Concurrent marking 12M->12M(16M) (2.347s, 2.348s) 1.590ms Pause Final Mark 12M->4M(16M) (2.348s, 2.351s) 2.599ms Concurrent evacuation 4M->5M(16M) (2.351s, 2.351s) 0.155ms Pause Init-Mark (2.926s, 2.928s) 1.869ms Concurrent marking 12M->12M(16M) (2.928s, 2.932s) 3.416ms Pause Final Mark 12M->9M(16M) (2.932s, 2.933s) 1.205ms マークが完了した時点で前回選択されたコレクシ ョンセットへの有効な参照はなくなっているので 、リージョンのメモリを解放することができる。 マークの情報を使って、新しいコレクションセッ トが選択される。 ログからもFinalMark のタイミングでメモリ使用 量が減っていることが読み取れる。
  20. 20. non-generational GC ❖ 世代別GC ➢ 一部領域をGCする際に経験則に基いて行う ➢ 「最近アロケーションした領域をGCしたほうが回収効率が良いだろう」 ❖ Shenandoah では FinalMark 時点でヒープ内の全てのオブジェクトの 生存状況が把握できている ❖ どのリージョンを回収すればよいかを考える際、仮説に基づく必要はない ShenadoahGC は世代別GCではない ❖ 世代別仮説にあてはまらないケースでも安定して動作する ❖ RememberedSet を持つ必要がない ❖ GCするたびにヒープ全体をマークする必要がある
  21. 21. まとめ ❖ ShenandoahGCではコピーGCをアプリケーションと並行に実行できる ❖ 並行退避を安全に行うための仕組み - BrooksPointer 退避先のオブジェクトが必ず参照されるようにする - ライトバリア 退避処理が終わる前に更新が行われないようにする - CAS 1つのオブジェクトが2回以上退避されないようにする ❖ ShenandoahGCのサイクル - 前回のサイクルのコレクションセットを解放している - マークフェーズで参照の移動をしている ❖ShenandoahGC は世代別GCではない
  22. 22. おまけ(FAQ) ❖ ShenandoahGC は試せますか? → Shenandoahのソースコード(JDK9)を mercurial から持ってきてビルドすれば、あとは -XX:+UseShenandoahGC オプションで利用できます。 ただし開発中なのでどのプラットフォームでも安定してビルドできるというわけではなかったりします。 今回はCentOS7の最新でうまくビルドできたので、うまくいかない場合はRedhat系ディストリビューショ ンの最新版で試すのが良さそうです。 ❖ ShenandoahGC の開発は進んでますか? → 2016年の後半は開発メンバーも増え、メーリングリストでのやりとりも活発でした。ソースコードを読 んでいても半年でずいぶんちゃんとしたなという印象です。一方でまだクリティカルなバグが普通に発生 したり、未実装の機能があったり(ex. ヒープダンプ出せません)するので、Production まではもう少しかか りそうです。 ❖ 色々処理が追加されているのでオーバーヘッドがあるのでは? → RememberedSetをなくしたというプラスはあるのですが、BrooksPointerもライトバリアもCASも少な からずオーバーヘッドがあります。2016年はじめに開発主要メンバーのRomanさんが試してみたところ だと、G1にはちょっと負けているようです(ポーズタイムはShenandoahの方がずっと短い)。 (参考) https://rkennke.wordpress.com/2016/02/08/shenandoah-performance/

×