Java HotSpot™ 仮想マシンのパフォーマンスの向上

階層型コンパイル

Java SE 7 で導入された階層型コンパイルによって、サーバー VM へのクライアント起動が短縮されます。通常、サーバー VM はインタプリタを使用して、コンパイラに渡されるメソッドについてのプロファイリング情報を収集します。階層型方式では、インタプリタに加えてクライアントコンパイラを使用してコンパイルバージョンのメソッドが生成され、それらが自身のプロファイリング情報を収集します。コンパイルされたコードはインタプリタよりもはるかに高速なため、プログラムのプロファイリング段階の実行パフォーマンスが大きく向上します。サーバーコンパイラによって生成される最終コードをアプリケーション初期化の早い段階で利用できる可能性があるため、多くの場合クライアント VM での起動速度を向上させることができます。また、階層型方式ではプロファイリング段階の高速化によってプロファイリングにかけられる時間が長くなるため、通常のサーバー VM よりも優れたピークパフォーマンスを実現でき、最適化の向上にもつながる可能性があります。

32 ビットと 64 ビットの両方に加え、圧縮 OOP がサポートされます (次のセクションを参照)。階層型コンパイルを有効にするには、java コマンドで -XX:+TieredCompilation フラグを使用します。

圧縮 OOP

Java HotSpot の専門用語である「OOP」 (Ordinary Object Pointer) は、オブジェクトへの管理ポインタです。OOP は通常、ネイティブマシンポインタと同じサイズ (LP64 システムでは 64 ビット) です。ILP32 システムでは、最大ヒープサイズは 4G バイトよりやや少なく、これは多くのアプリケーションにとって十分ではありません。LP64 システムでは、指定されたプログラムが使用するヒープが、ILP32 システムで実行する場合よりも約 1.5 倍大きくなることがあります。これほど必要なのは、管理ポインタのサイズが増大することが原因です。メモリーのコストは低いですが、最近では帯域幅およびキャッシュが不足しているため、4G バイト制限を解決するためだけにヒープサイズが大幅に増加するのは望ましくありません。

Java ヒープ内の管理ポインタは、8 バイトアドレス境界に整列されたオブジェクトを指します。圧縮 OOP は、(JVM ソフトウェア内のすべてではないものの多くの場所で) 管理ポインタを、64 ビット Java ヒープベースアドレスからの 32 ビットオブジェクトオフセットとして表します。これらはバイトオフセットではなくオブジェクトオフセットのため、最大 40 億のオブジェクト (バイトではありません)、または最大約 32G バイトのヒープサイズをアドレス指定するために使用できます。これらを使用して参照先オブジェクトを見つけるには、これらを 8 倍して Java ヒープベースアドレスに加算する必要があります。圧縮 OOP を使用するオブジェクトサイズは、ILP32 モードのそれに匹敵します。

デコードという語は、32 ビット圧縮 OOP が管理ヒープ内の 64 ビットネイティブアドレスに変換される処理を表すのに使われます。この逆の処理は、エンコードと呼ばれます。

圧縮 OOP は Java SE 6u23 以降でサポートされ、デフォルトで有効になっています。Java SE 7 では、-Xmx が指定されていないときの 64 ビット JVM プロセスおよび -Xmx の値が 32G バイト未満の場合に、デフォルトで圧縮 OOP が使用されます。6u23 リリースより前の JDK 6 でこの機能を有効にするには、java コマンドで -XX:+UseCompressedOops フラグを使用します。

ゼロベース圧縮 OOP (Ordinary Object Pointer)

64 ビット Java 仮想マシンプロセスで圧縮 OOP を使用する場合、JVM ソフトウェアは、仮想アドレスゼロから始まるメモリーを Java ヒープ用に予約するようにオペレーティングシステムに要求します。オペレーティングシステムがこのような要求をサポートしていて、Java ヒープ用のメモリーを仮想アドレスゼロで予約できる場合、ゼロベース圧縮 OOP が使用されます。

ゼロベース圧縮 OOP を使用することは、Java ヒープベースアドレス内で加算することなく、64 ビットポインタを 32 ビットオブジェクトオフセットからデコードできることを意味します。ヒープサイズが 4G バイト未満の場合、JVM ソフトウェアはオブジェクトオフセットの代わりにバイトオフセットを使用できるため、オフセットを 8 倍にすることも回避できます。64 ビットアドレスを 32 ビットオフセットにエンコードすると、それだけ効率的になります。

Java ヒープサイズが 26G バイト程度の場合は、Solaris、Linux、および Windows オペレーティングシステムでは通常、Java ヒープを仮想アドレスゼロに割り当てることができます。

エスケープ解析

エスケープ解析は、Java Hotspot Server コンパイラが新規オブジェクトの使用スコープを解析し、それを Java ヒープに割り当てるかどうかを決定するための技術です。

エスケープ解析は Java SE 6u23 以降でサポートされ、デフォルトで有効になっています。

Java Hotspot Server コンパイラは、次に記述する、フローインセンシティブエスケープ解析アルゴリズムを実装します。

 [Choi99] Jong-Deok Choi, Manish Gupta, Mauricio Seffano,
          Vugranam C. Sreedhar, Sam Midkiff,
          "Escape Analysis for Java", Procedings of ACM SIGPLAN
          OOPSLA  Conference, November 1, 1999

エスケープ解析に基づき、オブジェクトのエスケープ状態は次のいずれかになる可能性があります。

エスケープ解析のあとサーバーコンパイラは、スカラー置換可能オブジェクト割り当ておよび関連付けられたロックを、生成されたコードから除去します。また、サーバーコンパイラは非グローバルにエスケープするすべてのオブジェクトのロックも除去します。ヒープ割り当てを、非グローバルにエスケープするオブジェクトのスタック割り当てで置き換えることはありません

エスケープ解析のいくつかのシナリオを次で説明します。

NUMA コレクタの拡張機能

Parallel Scavenger ガベージコレクタは、NUMA (Non Uniform Memory Access) アーキテクチャーを持つマシンを利用できるように拡張されています。最新のほとんどのコンピュータは、メモリーの異なる部分にアクセスするためにかかる時間が異なる、NUMA アーキテクチャーをベースにしています。通常、システム内の各プロセッサには、アクセス遅延時間が小さく帯域幅の広いローカルメモリーと、アクセスがはるかに遅いリモートメモリーが搭載されています。

Java HotSpot 仮想マシンでは、NUMA 対応アロケータがこのようなシステムを利用するために実装されており、Java アプリケーションのためにメモリー配置を自動的に最適化します。このアロケータは、若い世代のヒープの Eden 領域 (新しいオブジェクトのほとんどが作成される) を制御します。アロケータはこの領域を、いくつかの領域 (それぞれが特定ノードのメモリー内に配置される) に分割します。アロケータは、オブジェクトを割り当てるスレッドがそのオブジェクトをもっとも使用する可能性があるという仮定に依存しています。新しいオブジェクトに最速でアクセスするために、アロケータは割り当て側スレッドからローカルな領域内にそれを配置します。領域は、さまざまなノードで実行されているアプリケーションスレッドの割り当て率を反映するために、動的にサイズ変更できます。これにより、単一スレッドアプリケーションであってもパフォーマンスを向上できます。また、若い世代の「From」および「To」 Survivor 領域、古い世代、および永続的世代では、ページインターリーブが有効になっています。これにより、すべてのスレッドの、これらの領域へのアクセス遅延時間が均一になります。

NUMA 対応アロケータは、Solaris™ 9 12/02 以降の Solaris オペレーティングシステム、および Linux kernel 2.6.19 かつ glibc 2.6.1 以降の Linux オペレーティングシステムで利用できます。

NUMA 対応アロケータは、-XX:+UseNUMA フラグと Parallel Scavenger ガベージコレクタの選択によって有効にできます。Parallel Scavenger ガベージコレクタはサーバークラスマシンのデフォルトです。Parallel Scavenger ガベージコレクタは、-XX:+UseParallelGC オプションを指定することで明示的に有効にすることもできます。

-XX:+UseNUMA フラグは Java SE 6u2 で追加されました。

注:Linux カーネルには、-XX:UseNUMA を使用して実行したときに JVM がクラッシュする既知のバグがありました。このバグは 2012 年に修正されたため、Linux カーネルの最新バージョンには影響しないはずです。カーネルがこのバグを持つかどうかを確認するために、ネイティブリプロデューサを実行できます。

NUMA パフォーマンスメトリック

SPEC JBB 2005 ベンチマークに基づいて 8 チップ Opteron マシンで評価したときに、NUMA 対応システムは次のパフォーマンス向上を示しました。


Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.