読者です 読者をやめる 読者になる 読者になる

JRockit JVMのガベージコレクションのまとめ

JRockit JVMガベージコレクションの仕組みを、公式のドキュメント読んでざっくりまとめてみました。
改めて調べてみると、HotSpot JVM とほとんど変わらないみたいです。既にご存知の方には釈迦に説法ですが、JVM のメモリ管理について初学者の方は、参考にしてみてください。

JRockit JVM が使用するメモリ領域

スタック領域や、パーマネント領域といった分け方もありますが、参照先では以下のような分け方で説明されています。

  • ヒープメモリ
    • 二世代のガベージコレクションが利用される場合、「ナーサリ(若い領域)」と「古い領域」の2つの領域(世代)に分割される *1
    • ナーサリ(若い領域)
      • 新しいオブジェクトの割り当てのために予約されている領域
    • 古い領域
      • 若いコレクションで昇格されたオブジェクトや、大規模オブジェクトが割り当てられる領域
  • ヒープメモリ以外のメモリ領域

若いコレクションと、古いコレクション

二世代のガベージコレクションを利用する場合、ガベージコレクションには、「若いコレクション」と「古いコレクション」とがあります。

  • 若いコレクション
    • ナーサリに保持された古いオブジェクトを、古い領域に「昇格(移動)」する処理
    • 古いコレクション(後述)や一世代のガベージコレクション(ナーサリのないヒープに対するガベージコレクション)よりも、格段に速くメモリ領域を開放できる
    • ナーサリの「保持領域」
      • ナーサリの一部に確保される領域
      • ここに保管されたオブジェクトは、直後の若いコレクションで昇格されない。このことにより、オブジェクトが割り当てられた直後に昇格してしまうのを防止する
  • 古いコレクション

小規模オブジェクトと大規模オブジェクト

JRockit JVM では、オブジェクト割り当て時に小規模オブジェクトと大規模オブジェクトが区別されます。

  • 小模オブジェクトと大規模オブジェクト

    • 通常 2 ~ 128 KB が区別の基準となる
    • 小規模か大規模化は、おおよそ以下の項目により決定される
  • 小規模オブジェクト

    • スレッドローカル領域(TLA)に割り当てられる
    • 二世代のガベージコレクションの場合、TLA はナーサリから確保される
  • 大規模オブジェクト
    • TLA に収まらないオブジェクト
    • 二世代のガベージコレクションの場合、古い領域に割り当てられる
    • 大規模オブジェクトの割り当てには、複数Java スレッド間の、オブジェクトキャッシュの同期を頻繁に行う必要がある *2

ガベージコレクション

二世代のガベージコレクションを利用している場合、ここでの記述内容は、古いコレクションに該当します。 マーク アンド スイープ モデルという、ガベージコレクションアルゴリズムについての説明です。

  • マーク アンド スイープ モデル
    • JRockit JVMガベージコレクションモデル
    • マークフェーズとスイープフェーズからなる
    • マークフェーズの動作
      • 現在使用されているオブジェクトを識別し、マークを付ける
      • 以下のようなオブジェクトが生存状態としてマークされる
        • Java スレッド、ネイティブ ハンドルおよびその他のルートソースから到達可能なオブジェクト
        • 上記のオブジェクトから到達可能なオブジェクト
    • スイープフェーズの動作
      • オブジェクトの木構造をたどることにより、生存状態としてマークされなかったオブジェクトを見つける。そのオブジェクトが配置されていた領域はフリーリストに記録され、新しいオブジェクトの割り当てに利用される。 *3
    • マークフェーズとスイープフェーズの組み合わせ方により、いくつかの方式がある

モースト コンカレント マーク アンド スイープ における処理の詳細

モースト コンカレント マーク アンド スイープ では、マーク処理とスイープ処理において、それぞれ4つのフェーズが順に実行されます。

モーストリ コンカレント マークの4つのフェーズ

名前 動作 Java スレッドの停止
初期マーキング 生存しているオブジェクトのルートセットを識別する あり
コンカレント マーキング ルートセットからの参照にしたがって、生存している残りのオブジェクトを検索してマークする なし
プレクリーニング コンカレント マーキング中のヒープの変更を識別し、生存しているオブジェクトを検索してマークする なし
最終マーキング プレクリーニング中のヒープの変更を識別し、生存しているオブジェクトを検索してマークする あり

モーストリ コンカレント スイープ の4つのフェーズ

名前 動作 Java スレッドの停止
スイープ(1回目) ヒープの半分をスイープする。この間、オブジェクトは残り半分のヒープ領域に割り当てられる なし
休止(1回目) 残り半分に切り替えるための休止 N/A
スイープ(2回目) 残り半分のヒープをスイープする。この間、オブジェクトは残り半分のヒープ領域に割り当てられる なし
休止(2回目) 同期、および統計の記録のための休止 N/A

世代別ガベージコレクション(若いコレクション)

二世代のガベージコレクションを利用している場合、ヒープメモリ上にナーサリが存在します。ナーサリの保持領域外のオブジェクトを古い領域に昇格するのが、若いコレクションです。
若いコレクションの間、Java スレッドは停止します。

*1:二世代のガベージコレクションが利用されない場合は、すべてのヒープが古い領域なのと同じことだと思われる

*2:Oracleの公式ドキュメントには、「大規模なオブジェクトの割り当てには Java スレッド間の同期をより頻繁に行う必要があります。」とありますが、たぶんこういうことでしょう。

*3:Oracleの公式ドキュメントには、「生存しているオブジェクト間のギャップを見つけるために、ヒープがトラバースされます。」とありますが、たぶんこういうことでしょう。

*4:コンカレント マーク アンド スイープとの違いは、マーク中のヒープの変更を識別して、選択的にマークし直す機構があることです。