torutkのブログ

ソフトウェア・エンジニアのブログ

「Tuning JavaSE for Throughput and Latency」セミナー

日本オラクル開催のセミナー「Tuning JavaSE for Throughput and Latency」を受講しました。
もともとはJavaOne Tokyo 2012のプログラムだったものが開催できず今日あらためて開催したとのことです。

スライドは、Charlie Hunt氏*1作成のもので、説明は日本オラクルのサポートエンジニアの方からでした。(講師の方のブログ発見)

内容はHunt氏が著した次の書籍に基づくものとのことです。

Java Performance (Java Series)

Java Performance (Java Series)

さて、スライドが300枚弱とまずその量に圧倒されました。5時間枠で流すので、情報の洪水状態でした。一応JavaVM(HotSpot VM)のガベージコレクションについては過去調べており、また最近は仕事でパフォーマンスチューニングをしていた経験もあるのですが、それでも消化不良気味でした。

講義内容は到底ブログに収まるものではないので、気になったことをメモするに留めます。

メモ

-XX:+PrintFlagsFinal

オプションで指定できる項目の確定値を標準エラー出力に表示します。デフォルトの値を確認するときに便利です。

32bit OS

32bit OSでヒープサイズを増やすと、物理メモリの上限(Windowsなら3GB前後)の大半をJavaで占め、OSや他のプロセスが動作するとページングが発生し性能が劣化します。

同一マシンで複数JavaVMを稼働

JavaVM(HotSpot VM)のエルゴノミクス(デフォルト)は、並行/並列GCスレッド数などを他にJavaVMが動いていない前提で決めるので、複数JavaVMを動かすときは明示的にオプションでスレッド数を減らすか、OSのパーティショニングが可能なら利用できるCPUを制限するなどの処置が必要です。

性能の基本方針
  • インスタンスを新世代領域に留めFull GCを起こさない
  • ページングを起こさない
GC選択の方針
  • まず、Parallel GCを使用する
  • いろいろ改善を図っても停止時間等が要件を満たせないなら、CMSやG1GCへ変更する
  • CMS、G1GCはマイナーGCの停止時間を減らせるが、スループットは低下する
  • CMSを使うときはParallel GCのときより旧世代ヒープを大きく取る(1.2倍目安)
  • CMSの場合、無理に新世代領域に留めず旧世代領域にインスタンスをプロモートさせ早めにFull GCをするのもよい
  • JavaVM複数に分割するのも有効なチューニング方法
  • G1GCは、JavaSE 7 u4から製品レベルのサポート
  • TrainGCはなくなった
  • 将来CMSはG1GCに置き換わるかも(今はG1GCが登場したばかりなので様子見)
  • CMSとG1GCの違いは3つ:コンパクションを行いフラグメントをなくす、使うのが簡単(CMSが難しすぎ)、予測性(≠ハードリアルタイム)。G1GCはコピー型で、CMSはコンパクションがなければコピーしない。
  • Incremental CMSというものがあるが、小さい構成向けでサーバーVMではまず使わない
新世代のエデンとサバイバースペース

今回のセミナーでは、サバイバースペースの大きさにまで言及していました。エデンとサバイバーの大きさについてはどう考えればよいか分からなかったので収穫でした。
ただし、そのチューニングは高度なトレードオフで一概には決められず、かなり深い話になります。

JavaVMの最適化
  • JavaSE 7 u4から--XX:+OptimizeStringConcatがデフォルトとなり、文字列の結合が最適化される
  • TLBのヒットミスを低減させるためには、ラージページを使う
  • スレッドスケジューリングでタイムスライスを使い切る前にロックなどが原因で自主的にコンテクストスイッチしてしまうVoluntary Context Switchが多く発生していれば、性能改善の余地あり
  • エスケープアナリシスがJavaSE 6u23(サーバーVM)でデフォルト有効に。scalaの性能向上に効果大とのこと。
GCの最適化
  • エデン、旧世代領域は、スレッド毎にTLAB(Thread Local Allocation Buffer)に分割され、ロックせずに領域を割り当てる。領域割り当てはエデンの場合10CPU命令程度のため高速。昔TLABは特定スレッドが過度に領域割り当てると他のTLABは空いているのにメモリ不足となるフラグメントが起きたが、今は自動調整でサイズを変えるので解消したとのこと。
  • CMSはフリーリストでメモリ管理しているのでプロモートが遅い
  • Initial Markはルートから直接たどれるもののみ処理(短い時間で済む)
  • printgcstatsツール(オープンソース
  • ライブデータサイズ(ヒープ)を計測し、ヒープサイズはライブデータサイズの3-4倍を取る
  • ライブデータサイズ(パーマネント)を計測し、パーマネントサイズはライブデータサイズの1.2-1.5倍を取る
  • ライブデータサイズ(ヒープ)の1-1.5倍を新世代領域に、残りを旧世代領域にする
64bitVMでのメモリ効率

ヒープサイズが<26GBのとき、-XX:+UseCompressedOopsを指定すると、オブジェクトを指すポインタが32bitで済むのでメモリ効率がよくなります。

メモリ性能
  • Javaはメモリインテンシブなプログラム
  • PageoutやScan rateが継続して発生しているとメモリ不足かも
  • スレッドが増えるとスタックサイズが大きくなるので注意
インターンした文字列がPermanent領域からヒープ領域へ

JavaSE 7からインターンした文字列がPermanent領域ではなくヒープ領域へ置かれるように変更

その他
  • 本番でもGCログを出すよう設定しよう
  • オブジェクトヘッダーのage(マイナーGCを生き残った回数)は昔5bit(最大31)だったのが、バイアスロックに1bit割譲したため4bit(最大15)となった

*1:OracleでJavaVMパフォーマンスのリード・エンジニア。なお、先月Oracleを退職したそうです。