[JRockit] Understanding Compressed References

このエントリはDavid Buckが寄稿してくれました。JVMの問題に関するディープな話題を今後数回に分けて取り上げます。今日はその第1回目。

Compressed references(参照の圧縮)はJRockit 64bit版で高速化するための最適化です(メモリを集約の負荷で最大15%)。このエントリでは、この機能がどういうもので、どのような効果があるのかを説明します。その上で、参照圧縮を有効にした場合に起こりうる問題について説明します。

背景
Javaの "Write Once, Run Anywhere"という設計ゆえに、32bitから64bit JVMへの移行は通常何も触らずに移行できます(JNIを使っていない限りは)。単にJVMを入れ替えれば、再コンパイルせずとも64bitプロセスとして実行することのメリット(巨大なJavaヒープサイズやCPUレジスタの数が増える、とか)を得ることができます。これはまさにJavaプラットフォームが約束している、「アプリケーションは新しいハードウェアやVMテクノロジーの先進性を、アプリケーションそのものを更新せずとも利用できる」ことに他なりません。
しかし、トレードオフがあります。通常は性能に関連することなので、32bitと64bit JVMの選択をする場合には考慮すべき内容です。ある状況では、コードサイズや64bitプロセスのポインタ長が増加するので、その結果としてキャッシュの効果が薄れたり、CPUメモリ帯域幅を大量に消費することがあります。アプリケーションは各々異なるので、負荷テストをしてどのJVMが最高の性能を出すかを調べる必要がありますが、同一Javaアプリケーションを実行した場合、32bit JVMのほうが64bit JVMに比較して良い性能を出すことが多いのです。
参照の圧縮機能は64bit JRockit JVM上で実行しているアプリケーションが64bit JVMのメリットを享受しつつ、32bit JVMの性能メリットをも享受することができるソリューションなのです。ヒープサイズを十分に小さくした状態で実行すると、圧縮された参照により、64bit JRockit JVMで32bitのオブジェクト参照(ポインタ)を利用できます。32bitポインタを利用することにより、より高速になるだけでなく、他のオブジェクトを参照するJavaオブジェクトを格納するために必要なヒープメモリサイズを減らすこともできるのです。

詳細
参照の圧縮機能がもたらすメリットを理解する必要はありません。 知っておくべき情報が欲しいだけなら、「利用方法」の章に飛んでもらってかまいません。詳細を知りたい方は、続きを読んで下さい。
どのように64ビットのアドレス空間で32ビットのポインタを使うのでしょうか。舞台裏の策略が関与して、アプリケーションに両方の長所を与えようとしています。
圧縮参照には3種類あって、それぞれがサポートする最大ヒープサイズ(4GB、32GB、64GB)に対応しています。
最も理解しやすいシナリオは、ヒープサイズが十分に小さく、アドレス空間の最初の4GBに全てのヒープが格納できる条件で、4GBの圧縮参照を利用することです。32ビットアドレス空間で指定可能な範囲内では、単純に32ビットポインタを使用してヒープ上のJavaオブジェクトを指すことができます。これは実装がもっとも簡単な圧縮参照の形式ゆえ、最も古い形式でもあります。JRockitは何年にもわたって4GBの圧縮参照を紹介しています。
4GBの壁を越えると少々事情は複雑になります。我々はもはや32bitのポインタを直接使ってヒープ内のすべてのオブジェクトを参照することはできません。我々は、JRockitの内部設計の恩恵を受けることになります。ヒープ上のすべての単一のオブジェクトは、8バイト境界に整列されます。言い換えると、すべての単一のオブジェクトのアドレスは常に8の倍数になります。ビット操作に慣れている方は、最後の3桁が常にゼロになることがすぐにわかると思います。言い換えれば、我々は、各32ビットのヒープアドレスの上位29ビットだけを「使って」いるのです。
これらの「未使用」のビットを利用する鍵は、右に3ビットで各アドレスを回転させてから、各ヒープアドレスを格納し、そして、逆参照が必要な場合には、後で左の3ビットを逆に回転させるというものです。これにより、32ビットのポインタを使って35ビットのアドレス空間(35ギガバイト)を使うことができます。このやり方は複雑に思えるかもしれませんが、このような回転は、1個のマシンコードの命令で実行され、各アクセスに対し、アドレスの回転が及ぼすパフォーマンスへの影響はごくわずかに近い(より単純な4GB以下の圧縮参照と比較すると、性能への影響は、せいぜい2%程度)。これらのタイプの圧縮参照は32GBまでの圧縮参照として知られています。
毎年、劇的にメモリとパフォーマンスのためにアプリケーションがJVMに要求する要件は劇的に増加しています。特にJ2EEサーバサイドアプリケーション向けに、これまで以上に野心的なプロジェクトと組み合わせ、より大規模で複雑なフレームワークを利用するため、ほんの数年前には想像を絶するような、パフォーマンスと機能を要求しています。ポイントのケース:32GBより大きなJavaヒープを使っているお客様が以前よりも増えています。こうしたお客様向けに、最後の参照圧縮のタイプ、64GBタイプがあります。
32GBと64GBの間のヒープサイズの場合は、上記のビット回転トリックのバリエーションを使います。 64GBの参照圧縮では、JRockitは16バイトバウンダリでオブジェクトを保存するよう制限します。
断片化(16バイトの倍数を消費しないオブジェクト間の無駄な空間)がほとんど増えないように代償を払うことにより、0が保証されている4bitを各32bitアドレスに割り当て、その結果、36bit(64GB)の範囲を扱うためにビットシフトできるようになっています。
ポイントは、圧縮参照が32bit値の全ての1桁ビットを使って、可能な限り大きなアドレス空間を参照しようとすることです。(64bitアドレス空間の)どこにJavaヒープを格納するか、アドレスをどうやって格納するか、といった仮定をすると、64bit JVM上で実行していても64bitポインタを利用することによるオーバーヘッドを避けることができます。

利用方法
ほとんどの利用者にとって、圧縮参照はすぐに効果を発揮します。実際に、既に何もしらないうちに使っているかもしれません。コマンドラインで指定した最大ヒープサイズ(-Xmx)に依存して、JRockitは自動的に参照圧縮を利用するようになっています。JRockit R28では、以下のようになっています。
-Xmx: <= 3GB  -- 4GB の参照圧縮を使用
-Xmx: <= 25GB -- 32GB の参照圧縮を使用
-Xmx: <= 57GB -- 64GB の参照圧縮を使用
過去のリリース(R27以前)では、4GBの参照圧縮のみがサポートされていました。より大きなヒープサイズ(64GB以下)を利用している場合、参照圧縮を活用できるJRockit R28へのアップグレードを検討したくなるかもしれません。JRockit R26.4から、4GB以下の全てのヒープサイズに対し、参照圧縮がデフォルトで有効です。
JRockitの標準の動作は、ほとんどのユーザーにとって理想的ですが、あなたが圧縮された参照を無効にしたり、使用する参照圧縮の種類を指定する(R28のみ)ことができます。JRockitのデフォルト動作のオーバーライドに関する詳細は、オンラインマニュアルを参照してください。

Oracle® JRockit Command-Line Reference Release R28
3 -XX Command-Line Options
-XXcompressedRefs
http://download.oracle.com/docs/cd/E15289_01/doc.40/e15062/optionxx.htm#BABCDECI

Oracle® JRockit Command-Line Reference Release R26/R27
-XX Command-Line Options
-XXcompressedRefs
http://download.oracle.com/docs/cd/E13150_01/jrockit_jvm/jrockit/jrdocs/refman/optionXX.html#wp1021022

ややこしいことなど
JVMが4GB以下で利用可能なアドレス空間が不足している場合、圧縮参照が利用できない場合が時折発生します。
(通常は、ある時点で32ビットのポインタによって参照されているため)アドレス空間の最初の4GBにのみ保存できる他のデータ構造やコードがあります。 4GBの参照圧縮を使用する場合は、全体のJavaヒープは、この貴重な4GB以下のアドレス範囲に格納されるため、OutOfmemoryErrors(OOME)がが発生する可能性があります。実際には、3GBは、4GBの参照圧縮を使用したいと思う最大のヒープサイズでしょう。

(64bitプロセスが予約可能な)アドレス空間は事実上無制限なので、OOMEが64bitプラットフォームで発生することはほとんどありません。64bit JRockitでOOMEが発生し、しかも4GBの圧縮参照を使用している場合、4GB以下のアドレス空間の枯渇が原因でOOMEが発生しています。
幸運にも、この問題は簡単に解決できます。最新のJRockitリリース(R28)では、(4GBの参照圧縮ではなく)32GBの参照圧縮の利用をJRockitに強制することで、この貴重なアドレス空間に格納すべき他のデータのために十分な余地を残して、4GBの範囲を超えて格納することができます。
$java -Xmx3584m  -XXcompressedRefs:size=32GB MyWonderfullJavaApp
過去のリリース(R27以前)では、4GBの参照圧縮のみがサポートされていたので、選択可能なオプションは、参照圧縮を無効にするしかできません。
$java -Xmx3584m -XXcompressedRefs=0 MyWonderfullJavaApp
R26やR27を利用されている方で、4GBの参照圧縮には大きすぎるヒープを使っている場合、 より大きなヒープサイズをサポートする参照圧縮が追加されたR28を使おうと、アップグレードを検討したくなるでしょう。

まとめ
圧縮参照機能は本当に64bit JRockit上で動作するアプリケーションの性能を向上させることができます。多くのアプリケーションにとって、スムースかに32bit JVMから移行し、多くのアプリケーションでは、実質的に性能に影響する事柄を修正すれば、32bit JVMからの移行をスムーズにすることができます。さらに良いことに、簡単に使え、すぐに効果を発揮しますので、実際には、既に恩恵を受けているかもしれませんね。

PS. HotSpot JVMでも同様の機能(CompressedOops)をサポートしています。


原文はこちら。
http://blogs.oracle.com/jrockit/entry/understanding_compressed_refer

0 件のコメント:

コメントを投稿