JEP 519 : Compact Object Headers

blog1.mammb.com


はじめに

JDK 24 にて実験的機能として公開されたコンパクト・オブジェクト・ヘッダは、JDK 25 で正式公開となります。

コンパクト・オブジェクト・ヘッダの導入により、以下のパフォーマンス向上が実証されています。

  • SPECjbb2015ベンチマークでヒープ領域が22%減少、CPU時間が8%減少
  • SPECjbb2015 が実行するガベージコレクションの回数が、G1コレクターとパラレルコレクターの両方で 15%削減
  • 高度に並列化された JSON パーサベンチマークで、実行時間が10%短縮


コンパクト・オブジェクト・ヘッダの有効化

コンパクト・オブジェクト・ヘッダは、以下のコマンドライン・オプションで有効化します(JDK24では-XX:+UnlockExperimentalVMOptionsが必要)。

$ java -XX:+UseCompactObjectHeaders ...

コンパクト・オブジェクト・ヘッダは、現在のところ、以下の場合は利用することはできません。

  • x64およびAArch64以外のプラットフォーム
  • 圧縮クラス・ポインターが無効化されている(-XX:-UseCompressedClassPointers)
  • HotSpot JVM がレガシースタックロッキング機構を使用する
  • JVMが8TBより大きいヒープを使用するように設定され、ZGCを使用しない


オブジェクト・ヘッダ

ヒープに格納されるオブジェクトにはメタデータがあり、HotSpot JVM はこれをオブジェクトのヘッダーに格納します。 オブジェクトヘッダーは以下のような用途で利用されます。

  • ガベージコレクション - 転送ポインタを保存し、オブジェクトの年齢を追跡
  • 型システム - オブジェクトのクラスを識別し、メソッド呼び出し、リフレクション、型チェックなどに使用
  • ロック - 関連する軽量ロックと重量ロックの情報を格納
  • ハッシュコード - 一度計算されたオブジェクトの安定したIDハッシュコードを格納

ヘッダーのサイズは一定で、オブジェクトの型、配列の形状、コンテンツに依存しません。 64ビット版 HotSpot JVM では、オブジェクトヘッダーは JVM の設定に応じて 96bit(12byte) から 128bit(16byte)の範囲を占めます。

Javaプログラム内のオブジェクトは一般的に小さい傾向があり、多くのワークロードの平均オブジェクトサイズが256~512bit(32~64byte)であることが示されています。 これは、オブジェクトヘッダーだけでライブデータの20%以上を占める可能性があることを意味します。 したがって、オブジェクトヘッダーサイズをわずかに改善するだけで、フットプリント、データの局所性、そしてGC負荷の大幅な削減につながる可能性があります。


従来のオブジェクト・ヘッダレイアウト

オブジェクト・ヘッダーのレイアウトは、マーク・ワードとクラス・ワードに分かれています。

マーク・ワードが最初に来て、マシン・アドレスのサイズを持ちます。

Mark Word (normal):
 64                     39                              8    3  0
  [.......................HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.AAAA.TT]
         (Unused)                      (Hash Code)     (GC Age)(Tag)

状況によっては、マーク・ワードは別のデータ構造へのタグ付きポインターで上書きされます

Mark Word (overwritten):
 64                                                           2 0
  [ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppTT]
                            (Native Pointer)                   (Tag)

この場合は、タグビットは、ヘッダーに格納されているポインターのタイプを記述する(必要であれば、元のマーク・ワードは、このポインタが参照するデータ構造内に保存(変位)され、元のヘッダーのフィールド、すなわち、ハッシュ・コードと年齢ビットは、変位されたヘッダーに到達するためにポインタをデリファレンスすることによってアクセスされる)。 。

マーク・ワードの後にはクラス・ワードがあり、これは、圧縮クラス・ポインタが有効かどうかによって、次のどちらかのレイアウトとなります。

Class Word (uncompressed):
64                                                               0
 [cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc]
                          (Class Pointer)

Class Word (compressed):
32                               0
 [CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC]
     (Compressed Class Pointer)

クラス・ワードは決して上書きされません(オブジェクトの型情報は常に利用可能であり、型のチェックやメソッドの呼び出しに追加のステップは必要ない)。


コンパクト・オブジェクト・ヘッダのレイアウト

コンパクト・オブジェクト・ヘッダでは、クラス・ポインタを圧縮した形でマーク・ワードに含めることで、マーク・ワードとクラス・ワードの間の分割を取り除きます。

Header (compact):
64                    42                             11   7   3  0
 [CCCCCCCCCCCCCCCCCCCCCCHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHVVVVAAAASTT]
 (Compressed Class Pointer)       (Hash Code)         /(GC Age)^(Tag)
                              (Valhalla-reserved bits) (Self Forwarded Tag)

Valhalla-reserved bits は Valhalla プロジェクトでの将来利用を想定して4bitが予約されます。