HP-UX Java パフォーマンス・チューニングガイド

85
HP-UX Java パフォーマンス・チューニング Whitepaper

Transcript of HP-UX Java パフォーマンス・チューニングガイド

Page 1: HP-UX Java パフォーマンス・チューニングガイド

HP-UX Java パフォーマンス・チューニングWhitepaper

Page 2: HP-UX Java パフォーマンス・チューニングガイド

目次1 はじめに . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5

1-1 チューニング・ルール [] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5

1-2 チューニング手順 [] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7

1-2-1 評価(Assess) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7

1-2-2 測定(Measurement) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8

1-2-3 分析(Analyze) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9

1-2-4 特定(Identify) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9

1-2-5 変更(Tune) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9

1-3 HP Javaパフォーマンスツール . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9

2 パフォーマンス問題の原因と対策 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10

2-1 hp-uxカーネル・パラメータ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10

2-2 hp-uxネットワーク・パラメータ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12

2-3 hp-uxおよびJavaのパッチ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13

2-4 ガベージ・コレクション . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13

2-4-1 ガベージ・コレクションの分析 -Xverbosegc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15

2-4-2 Javaヒープ・メモリの構造 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17

2-4-3 Javaガベージ・コレクションのメカニズム . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19

2-5 リソース競合 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .25

2-5-1 スレッド・スタック・トレース kill -3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29

2-5-2 スレッド・ロック問題 -HPjmeterの使用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .30

2-5-3 スレッドの優先度 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .32

2-6 リソースを大量に消費するメソッド・コール . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .33

2-7 メモリ・リーク . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .34

2-8 HPjmeterを使ったメモリ・リテンションの解析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .37

2-8 ベンチマークとHot Spot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .42

2-9 その他のJVMオプション . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46

3 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .47

4 参考文献 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .48

(付録A)ツールを用いた簡単なチューニング例 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .49

パフォーマンス問題例 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .49

HPjmeterを使った解析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .49

HPjtuneを使った解析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .52

チューニング作業 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .55

チューニング結果 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .56

Page 3: HP-UX Java パフォーマンス・チューニングガイド

2

(付録 B)HP-UX HotSpot JVMのPermanent領域について . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .57

オプションによるサイズの指定 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .57

Permanent Space不足によるエラー . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .58

クラスローダによるPermanent領域へのクラスのロード . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .58

Permanent領域とGarbage Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .59

Permanent領域のチューニング . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .60

(付録C)HP-UX上でのCヒープとJavaヒープ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .61

GlancePlusではモリ消費量がどのように見えるか . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .61

(付録D)Javaヒープサイズの制限について(PA-RISC版SDK) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .63

(付録E)Javaスタックトレースによるリソース競合検出 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .65

デッドロック . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .68

(付録 F)OutOfMemoryErrorについて . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .69

Javaヒープ(Old領域)不足 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .69

仮想メモリ不足 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .70

カーネルパラメータ値が小さすぎる . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .70

(付録G)SDK 1.4での新しいガベージコレクション(GC)) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .72

パラレルGC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .72

コンカレントGC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .73

(付録 H)HP-UX 11i Version 2(11.23)、Java2 1.4パフォーマンス関連情報 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .74

Java2プラットフォーム . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .74

パフォーマンスチューニングツール . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .74

カーネルコンフィグレーション . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .74

Java2 1.4パフォーマンス関連強化機能 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .76

Page 4: HP-UX Java パフォーマンス・チューニングガイド

5/5

1 はじめに

このドキュメントは、Java関連のパフォーマンス問題に有用なトラブルシューティング情報を提供し、hp-ux上の Javaをチューニングし、パフォーマンスをより向上させることを目的としています。 本ドキュメントはチューニング手法の入門として利用されることを目的として

おり、全ての詳細な内容を含むものではありません。詳細については「4 参考文献」のセクションに書かれているいくつかの Javaパフォーマンスに関する本や文献が参考になるでしょう。 本書は Hot Spot Runtime Compiler 1.3.1 以降を含む HP-UX Java Developer’s Kit (SDK) 1.3.X を対象として書かれており、プラットフォームとしては PA-RISC,Itanimumの両方を対象としています。但し、バージョンによる機能の違いもありますので、バージョン毎の情報に関する詳細については、SDKのバージョンのリリースノートを参照してください。 ここで記述されているほとんどの内容は以下の webサイトにおいて、より詳細に解説されています。 http://www.hp.com/go/java (java products for hp-ux)

➟ information library ➟ brochures and articles

➟ performance tuning java on hp-ux

1-1

チューニング・

ルール [1]

パフォーマンス・チューニングにはいくつかの基本的な法則があり、これらは

どのようなシステムに対しても適用できる一般的なものです。技術的な内容を

論じる前に、まずこれらの普遍的なルールをいくつか紹介します。

ルール 1:

システムのパフォーマンスは、多くの問題がお互いに複雑に依存している。

システムは多くのコンポーネントが協調して動作しています。そしてこれらは

お互いに複雑な関係を結んでいます。そのため、あらかじめパフォーマンスの

ボトルネックを特定することは非常に困難です。 このような依存性は 2番目ルールを導きだす要因ともなっています。

ルール 2:

パフォーマンス・チューニングは常にトレード・オフを伴う。

[1] HP-UX Tuning and Performance, Robert F. Sauers and Peter S. Weygant, Prentice Hall

Page 5: HP-UX Java パフォーマンス・チューニングガイド

6/6

システム・チューニングにはOS動作の仕組みやパフォーマンスに影響を与える依存関係の理解が必要です。ある特定の部分を変更することは、他の部分から

その恩恵を奪うことを意味します。例えば、メモリ利用率を向上させるための

チューニングはファイル・システムのパフォーマンスを低下させることになる

かも知れません。アプリケーションの要求や、メモリとファイル・システムの

相互作用を理解することにより、最適なトレード・オフを選ぶことができます。 このようにボトルネックが存在する場合には、リソースと、リソースに対する

要求のバランスをうまく取る必要があります。ボトルネックを解消するために

は、リソースを増加させるか、あるいは要求を減らすか、そのどちらかしかあ

りません。そういった意味でもリソースの使用状況を観察することは非常に有

用です。 リソース使用状況を観察する際に重要なことは次のルールで示されます。

ルール 3:

観察されるシステムに影響を与えることなくパフォーマンスを観察することは

できない。

計測をすることはそれ自体リソースを消費します。また使用するツールの種類

によって、その消費量は異なります。例えば、gpm(Glanceのグラフィカル・バージョン)は、vmstatや glanceと比べてはるかに大きいメモリを必要とします(5MB以上)。 ツールはリソースを消費するだけでなく、システムそれ自体をも変えてしまう

ことに注意してください。ツールを実行するのに十分なメモリを持たないシス

テムの場合、ツールがメモリを余分に要求することにより、スラッシング(物

理メモリとスワップ領域の間で頻繁にページをやり取りする作業)が発生する

かもしれません。こうなると観察されるシステムはもう元のシステムではあり

ません。 システムへの影響がどの程度許されるかというのはツールを選ぶ上での重要な

用件です。状況によって様々なツールを使い分けることが求められます。これ

は以下の最後のルールへとつながります。

ルール 4:

パフォーマンス問題を解決するために必要な全てのデータを提供してくれる万

能ツールは存在しない。

どのような状況でも問題を解決してくれる完璧なツールは存在していません。

いろいろな状況に対応できるようになるためには、たくさんのツールに習熟す

るしかありません。また、複数のツールを使用することで情報をクロス・チェ

ックすることができ、ツールが本当に正しい情報を提供しているか確認するこ

とも可能となります。

Page 6: HP-UX Java パフォーマンス・チューニングガイド

7/7

1-2

チューニング手順 [2]

問題を解決することに大きなプレッシャーがかかっている場合は、十分な測定

や分析をすることなく安易に対応を取ってしまいがちです。ほとんどの場合、

安易な推測は悪い結果を招きます。 こうした場当たり的な対応を行う代わりに、以下のような一般的なパフォーマ

ンス・チューニング手法に従ってください。この手順を図示したのが図 1 です。

► 評価(Assess) ► 測定(Measure) ► 分析(Analyze) ► 特定(Identify) ► 変更(Tune)

1-2-1 評価(Assess) 評価はまず多くの質問に答えることから始まります。これらの質問に答えるこ

とで何を、どこまでチューニングするのかといった基本的な事柄を理解する助

けになります。以下の項目はこの段階で学ぶ必要のあるものです。

► システム構成 ► アプリケーション・デザイン ► パフォーマンスの目標 ► ピーク負荷、またその期間 ► システムあるいはアプリケーションで行われた変更点 ► パフォーマンス問題の発生する期間 ► 各種オプション

[2] HP-UX Tuning and Performance, Robert F. Sauers and Peter S. Weygant, Prentice Hall

start

assess

measure

interpret & analyze

performance meets criteria?

update/ tune

end

change in workload

yes

yes

no

no

図 1 チューニング手法

Page 7: HP-UX Java パフォーマンス・チューニングガイド

8/8

パフォーマンス分析において最初にとる行動はおそらくユーザー・トランザク

ションが通過する全てのコンピューターのマシン構成を書き留めることです。

必要となる情報は、

► コンピューターの CPU数と、それら CPUのスピード (CPU数は Glance/gpmで見ることができます)

► メイン・メモリの容量 (同じく Glance/gpmで確認可能)

► ディスク・スペース (使用領域と未使用領域、dfコマンドの出力)

► オペレーティング・システムのチューニング可能なパラメータ(HPjconfigツールで確認可)

► オペレーティング・システムに適用されるべきパッチ (これも HPjconfigで可)

► 使用されている JVMのバージョン (”java –version”コマンドで入手)

► JVMに指定しているオプション (-Xms <value> や-Xmx <value> など)

Javaバージョン情報の例として、”java –version”コマンドの出力を以下に示します。

java version "1.3.1.01-release" Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1.01-

release-010816-12:37) Java HotSpot(TM) Server VM (build 1.3.1 1.3.1.01-release-010816-

13:34-PA_RISC2.0 PA2.0, mixed mode)

これらのバージョン情報は、Javaプログラムのパフォーマンス特性を評価する上で参考になります。いくつかのパフォーマンス問題は、単純に最新のバージ

ョンの Java SDK にアップグレードすることや、最新のパッチをオペレーティング・システムに適用すること、Javaランタイムに正しいオプションを指定することで解決することもあります。 最新のバージョンの Java SDKにアップグレードし、最新のパッチ・セットを適用することが推奨されています。次のセクション(hp-ux カーネル・パラメータ)で詳しく解説する HPjconfigツールは、正しい hp-uxパッチがインストールされているかどうかをチェックするためも使用することができます。

$ java –classic ClassName

上は Javaバーチャル・マシンの誤った使い方を示しています。この例では、サーバー・サイドの長期稼動型アプリケーションには推奨されていない、古くて

最適化が不十分な Java ランタイム・バージョンが使用されています。この場合、デフォルトの Javaランタイム(HotSpot)の使用が推奨されています。

1-2-2 測定(Measurement) パフォーマンス・チューニングにおける次の段階は測定です。ここでは実際の

システム、アプリケーションのパフォーマンスを測定します。

Page 8: HP-UX Java パフォーマンス・チューニングガイド

9/9

hp-ux上の Javaパフォーマンス測定では、sar、topなどの標準的なツールに加えて以下のような方法を使用することができます。

► Glance/gpm パフォーマンス測定ツール ► JVMオプション –Xverbosegc ► JVMオプション –Xeprof と HPjmeterツール ► スレッド・スタック・トレース kill –3

これらのツールを使って情報を集めるには時間がかかる場合もありますが、測

定と分析は必ずシステムに変更を加える前に行っておく必要があります。

1-2-3 分析(Analyze) 測定が終わった後は、収集したたくさんのデータを分析しなければなりません。

ツールによってデータの出力形式は異なり、それらの複雑なデータを正確に解

釈する必要があります。これは簡単な作業ではありません。システムではいろ

いろな要素がお互いに複雑に作用していることを思い出してください。 データの分析方法については、「2 パフォーマンス問題の原因と対策」のセクションで具体的に説明します。

1-2-4 特定(Identify) データの解釈、分析を行うのはシステムにボトルネックが存在しているかどう

かを見極めるためです。何らかのボトルネックが存在することがわかれば、パ

フォーマンスを向上させるためのチューニングに取り掛かることができます。

しかし場合によっては、そのボトルネックを緩和した後に、新たなボトルネッ

クが発生してしまうこともあります。

1-2-5 変更(Tune) ボトルネックが特定されると、その後に取るべき道は 2つあります。リソースを増加させるか、あるいはそのリソースへの要求を減らすかです。どちらにし

ても一度に変更するのは必ず 1箇所に限定してください。2箇所以上変更してしまうと、何がパフォーマンスに影響を与えたのかわからなくなります。 また行った変更については逐次ノートなどに記録するようにしてください。変

更の履歴を取ることにより混乱を避けることができます。

1-3

HP Java

パフォーマンスツール

HPでは各チューニングステップ(Assess, Measurement, Analyze, Identify, Tune)をサポートする各種ツールを提供しています。次表は各ツール(GlancePlus,HPjmeter,HPjtune,Hpjconfig,OutofBox)が利用されるチューニングステップ、調査対象、チューニング対象を示したものです。

Page 9: HP-UX Java パフォーマンス・チューニングガイド

10/10

HP Java パフォーマンスツール

2 パフォーマンス問題の原因と対策

Javaアプリケーションのパフォーマンスに影響を与える要因はたくさん考えられますが、このセクションではその中でも代表的なものをいくつかピックアッ

プして説明していきます。それぞれの項目では基本的な解決方針についても触

れられますが、効率的なチューニングを行うためには、その背後にある動作の

仕組みを理解することが不可欠です。そうした技術的なバックグラウンドにつ

いても可能な限り説明します。

2-1

hp-ux カーネル・

パラメータ

Javaプログラムが動作するオペレーティング・システムを適切にセットアップすることは極めて重要です。これはあらゆるチューニングを行う前にまず確認

するべきことです。 Java環境向けパラメータセット(初期インストール時の第一ステップとしてのパラメータセット)を自動的に設定するために HPでは Java out-of-box とよばれるツールを提供しています。具体的にはこれをインストールすることにより、

カーネル・パラメータを Java環境に適切なものへと変更し、カーネルを再構成

Java ヒープサイズ

Java オプション Java ヒープとGC動作の

詳細 ○

HPjtune

パッチ, ネットワークとカー

ネルのパラメータ

パッチ, ネットワークとカ

ーネルのパラメータ ○

HPjconfig

Java ヒープサイズ

Java オプション

Java アプリケーション

ボトルネックの特定

Java ヒープとGC

Java スレッド

グラフ – CPU, 時間, メソッドコール

ロック・コンテンション

Objectの生成

HPjmeter

カーネルパラメータ

システムパラメータ

メモリサイズ

Java オプション

Java アプリケーション

CPU

I/O

メモリ

ネットワーク

プロセス, スレッド,

システムコール, 競合

GlancePlus

チューニング対象調査対象 TuneAnalyze and

Identify

Measure

Assess

カーネルパラメータ ○ ○ OutOfBox

Page 10: HP-UX Java パフォーマンス・チューニングガイド

11/11

図 2 HPjconfig の起動画面

し、システムのリブートを行います(同時にスタートアップ・スクリプトもイ

ンストール)。 http://www.hp.com/go/java ➟ hp products for Java 2 (PA-RISC)

➟ Java out-of-box tool for hp-ux さらにアプリケーション環境に応じて、チューニング可能なパラメータに最適

な値をセットするために利用できる便利なツールが hpより提供されています。 HPjconfigと呼ばれるこのツールはそれ自体が Javaプログラムであり、以下のwebサイトより無償でダウンロードできます。 http://www.hp.com/go/java (java products for hp-ux)

➟ information library ➟ technical documentation

➟ HPjconfig configuration tool for hp-ux 11 and 11i 以下のようにして HPjconfigを起動します。

$ java –cp HPjconfig.jar:HPjconfig_data.jar HPjconfig

このツールでは、コンピュータのモデルがチェックされた後、”Application Server”、”Web Server”、”WAP Server”などのアプリケーション・タイプが選択可能です。その後、hp-ux チューニング可能カーネル・パラメータの推奨値が提示されます。これらの値はファイルに保存することも可能です。スーパーユー

ザー、あるいはシステム・マネージャーは SAM(システム管理ツール)を使用

してオペレーティング・システムにこれらの値を適用します。図 2は HPjconfig の起動時の画面を示しています。このツールは現在の hp-ux に適用されているパッチが Javaに最適かどうかもチェックします。 図 3はあるアプリケーション・タイプに対するカーネル・パラメータの推奨値が示されています。

Page 11: HP-UX Java パフォーマンス・チューニングガイド

12/12

HPjconfig の ”Recommended Tuned Value”に表示される値はあくまで推奨であることに注意してください。コンピュータ上に複数のアプリケーション・プロセ

スが存在する場合や、データベース・プロセスが走っている場合には、最適な hp-ux カーネル・パラメータはマシンを共有している Javaプログラムと他のプログラムとの間で調整される必要があります。 Javaプログラムにおいて重要となる hp-ux カーネル・パラメータは以下のとおりです。

max_thread_proc 一つのプロセスが持つことのできるスレ

ッドの最大数

maxdsiz プロセスのデータ・セグメントのサイズ

maxfiles プロセスがオープンすることのできるフ

ァイルの最大数(ソフト・リミット)

maxfiles_lim プロセスがオープンすることのできるフ

ァイルの最大数(ハード・リミット)

ncallout I/O ペンディング・タイムアウトの最大値

nfile コンピュータ・システムでオープンでき

るファイルの最大値

nkthread コンピュータ・システムでサポートされ

るカーネル・スレッドの最大数

nproc コンピュータ・システムで走らすことの

できるプロセスの最大数

図 3 HPjconfig カーネル・パラメータの推奨値

Page 12: HP-UX Java パフォーマンス・チューニングガイド

13/13

2-2

hp-ux ネットワーク

・パラメータ

クライアントの接続がタイムアウトになったり、パケット・エラーの比率が高くなっている場合は、ネットワーク・パラメータtcp_conn_request_max(デフォルトの値は 20)が小さすぎる可能性があります。まず、アプリケーションの実行中に次のコマンドを使用して問題を調べます (あるいは、glanceでパケット・エラーの比率を観察できます)。

# netstat –p tcp

“connect requests dropped due to full queue”の等の値が増加している場合は、次のコマンドで現在のパラメータ値を調べます。

# ndd –get /dev/tcp tcp_conn_request_max

次に アプリケーションを実行する前に、次のコマンドを発行してより大きな値を設定します (HPjconfigを使って設定することもできます)。

# ndd –set /dev/tcp tcp_conn_request_max 2048

これによって問題が改善した場合は、リブート後もこの設定が有効になる

ように、/etc/rc.config.d/nddconf内でこの値を設定するようにしてください。

2-3

hp-ux および Java

のパッチ

最適なシステムを構成する上で適切なパッチを適用することは避けてとおるこ

とはできません。オペレーティング・システムのバージョンや、システムにイ

ンストールされている Java のバージョンによって必要なパッチは異なりますが、HPjconfigツールを使うことで、現在のシステムに必要となるパッチを自動的に検出し、表示してくれます。 この情報は hpの webサイトより入手することもできます。 http://www.hp.com/products1/unix/java/infolibrary/patches.html

2-4

ガベージ・コレク

ション

Javaプログラムはオブジェクトを必要とし、それらは処理を行うためにメモリを占有します。これらのオブジェクトは、プログラム実行時の最初に作成され

る場合もあれば、実行している間に作成されることもあります。Java言語において新しいオブジェクトが作成されると、ヒープと呼ばれる領域にそのための

メモリが割り当てられます。 以下のコマンドはデフォルトのヒープ・サイズで JVMを起動します(オプションを指定しない)。

Page 13: HP-UX Java パフォーマンス・チューニングガイド

14/14

$ java ClassName (ClassName はアプリケーションのクラス名)

以下のコマンドはヒープの最大値を 240 MB に制限して JVMを起動します。

$ java –Xmx240m ClassName

もしこの JVM が 240 MB 以上のメモリをオブジェクトに割り当てようとすると、JVM は”OutOfMemoryException”を発し、プログラムの実行を停止するでしょう。 Java プログラマは、C や C++ のプログラマと異なり、これらヒープを明示的に割り当て解除、あるいは解放する必要がありません(C/C++ プログラマは ”delete” 操作を行う必要があります)。 これはプログラマによってではなく JVM自身によって行われます。JVMは、オブジェクトがプログラムのどこからも参照されていないことを確認し、そのオ

ブジェクトがもう使用されていないということを決定します。この使われてい

ないオブジェクトを取り除くことを“ガベージ・コレクション”と呼び、JVMの中では定期的に、独立した活動として機能します。この頻度は、ヒープ・サ

イズの需要や他の様々な要素によって決定されます。 このメモリ管理手法の注目すべき点は、JVMが一度ガベージ・コレクションが必要だと判断すると、他のすべてのスレッド(Javaバイト・コード実行中、コード変換中、コンパイル中を含む)が安全なポイントに置かれ、ガベージ・コ

レクションが行われている間、停止されます。これはどのタイミングで実行さ

れようとも Javaプログラムのパフォーマンスに決定的な影響を与えます。 この理由により、Javaプログラムのパフォーマンスを分析する上でガベージ・コレクションの振る舞いを把握することは常に重要となります。 図 4は 8 CPUのコンピュータ上で 1つの JVMを実行している最中にガベージ・コレクションが発生した際の瞬間的な影響を示したものです。ここに見られる

ようにただ一つの CPUのみが高負荷状態となっており、これがガベージ・コレクションです。残りの CPUはほぼ使用されていない状態となっています。

図 4 ガベージ・コレクション作動時の CPU負荷状態

Page 14: HP-UX Java パフォーマンス・チューニングガイド

15/15

このため過度なガベージ・コレクションの発生は、Javaプログラムのスローダウンの大きな原因となります。JVM にはヒープ関連のオプション(-Xms、-Xmx、-Xmn、-XX:SurvivorRatio)を明示的に指定し、ガベージ・コレクションの発生をコントロールするべきです。しかし、まず最初の一歩は JVMで過度に発生しているガベージ・コレクションを見つけることです。 ガベージ・コレクションが煩雑に発生しているかどうかは Glance/gpmのメイン・ウインドウをみることによって手掛かりを得ることができます。図 5に見られるような突発的な CPUパターンは不要なガベージ・コレクションが発生していることを高い可能性で示しています。また図 5では、ユーザー・プログラム・タイムが時おり大きく落ち込んでおり、ガベージ・コレクションが CPUパワーを奪っていることが見てとれます。

2-4-1 ガベージ・コレクションの分析 –Xverbosegc ガベージ・コレクションの発生を確認することは hp-uxの Javaでは簡単に行うことができます。以下のように JVMのオプションとして”-Xverbosegc”を指定してください。

$ java –Xverbosegc:file=myfile.out ClassName

これにより”myfile.out”というファイルにガベージ・コレクションのデータを保存することができます。出力するファイルの名前は自由に変更できます。

Spiky CPU

pattern

図 5 ガベージ・コレクション発生時の典型的な CPUパターン

Page 15: HP-UX Java パフォーマンス・チューニングガイド

16/16

Note:このオプション指定時の JVMにおけるパフォーマンスの低下は非常に小さなものです(ディスクに出力ファイルを書き込む影響を除く)。必

要な場合は実稼動のシステムに使用することも可能です。

以下のように、出力データ(上の例での”myfile.out”ファイルの内容)はそのままの状態では読み取ることは困難です。

<GC: -1 21.040560 1 824 1 13369144 0 13369344 0 1703936 1703936 0 1206120 50331648 4793888 4793888 4980736 0.124737 > <GC: -1 24.040994 2 80 1 13369344 0 13369344 1703936 1703936 1703936 1206120 5058264 50331648 5032840 5032840 5242880 1.896397 > <GC: 2 28.602708 1 216 1 1109352 0 13369344 1703936 0 1703936 5058264 6780520 50331648 5242688 5242688 5242880 0.870284 > <GC: -1 45.500549 3 656 32 13369136 0 13369344 0 805256 1703936

6780520 6780520 50331648 8720800 8720800 8916992 0.055405 >

この出力ファイルには、ガベージ・コレクションのタイミングや範囲など様々

な情報を示す 19の列(カラム)があります。それぞれのカラムの意味は、次のコマンドで確認することができます。

$ java –Xverbosegc:help

この出力ファイルを見やすく整形するために、以下の webサイトより ”processVerboseGC.awk” ファイルをダウンロードすると良いでしょう。 http://www.hp.com/go/java (java products for hp-ux)

➟ information library ➟ brochures and articles

➟ performance tuning java on hp-ux ➟ tools ➟ -XverboseGC (JVM profiling option) ➟ -XverboseGC script

そして以下のコマンドを実行してください。

$ cat myfile.out |awk –f processVerboseGC.awk > output.txt

このコマンドは次に示すような、はるかにユーザー・フレンドリーな出力ファ

イルを生成します。

GC: Full GC required - reason: Old generation expanded on last scavenge

GC: Full 1.985317 s since last: 1.985317 s gc time: 96 ms eden: 1834928->0/3670016 survivor: 120576->0/262144 tenure: 2 old: 3204992->2356088/3928064 GC: Scav 2.190710 s since last: 0.205393 s gc time: 4 ms eden: 3669968->0/3670016 survivor: 0->120720/262144 tenure: 32 old: 2356088->2356088/3928064

これらのデータはプログラム実行中に発生した 3回のガベージ・コレクションを順番に示しています。それらが発生した時間は、プログラム開始から経過し

た時間として、”Full” あるいは ”Scav” の次に記述されます。 ”eden”や”survivor”、”tenure”、”old”といった言葉の意味がわからなくても、多

Page 16: HP-UX Java パフォーマンス・チューニングガイド

17/17

くの情報を読み取ることができます(これらの単語の意味はこの後で説明しま

す)。 “Full” ガベージ・コレクション(GC)イベントは、”Scavenge” あるいは ”Scav” ガベージ・コレクションと比べて常に時間がかかります。煩雑に”Full GC” が発生している場合(例えば 1分に1回かそれ以上)には、何らかの対策を講じる必要があります。

“Full GC required – reason : call to System.gc() or Runtime.gc()”

このデータに上のような記述があった場合には、ユーザーあるいはライブラリ

の Javaコードが明示的にガベージ・コレクションを呼び出していることがわかります。これは必ず避けなければなりません。コードから System.gc() や Runtime.gc() の呼び出しを取り除くか、JVMオプションに ”-XX:+DisableExplicitGC” を指定することによりこの問題を解決することができます。

$ java –XX:+DisableExplicitGC ClassName

“Full” あるいは “Scav” のすぐ後に続く数字はプログラムが開始された後の経過時間です。この時間(単位は秒)はプログラムの実行に従って累積され、その

イベントがどの時点で起こったのかを教えてくれます。 “since last” の後には、直前の GCが発生した後どのくらい時間が経過しているかを示す数字が続きます。”gc time” は、この GCイベントが完了するまでに要した時間を意味します。これらの数字には特別に注意しなければなりません。

一般的に正常な JVMは Scavenge GC より少ない回数の Full GC を実行します。Scavenge GC は 0.5秒以上の間隔をあけて分割して実行され、それぞれに 300 – 400 ミリ秒以上の時間がかかることはありません。 もし Scavenge GC が 1分以上行われている場合は、Javaバイト・コードを実行しているスレッドが少なくとも 1分は停止していることになり、パフォーマンスの問題を抱えていることになります。この問題を解決するためには、ヒー

プ・メモリの使用法を JVMオプション(-Xms、-Xmx、-Xmnなど)を指定することによって変更します。ヒープ・メモリのどの領域が利用不足なのか、あるい

は逆に利用されすぎているのかということを理解した後は、これらの値を適切

に変更するべきです。この点については次のセクションで解説します。 Full GC はヒープ領域のサイズに応じて数分かかることもあります。通常は、Full GC が 1分以内に終了し、Scavenge GC と比較して少ない頻度で起こるように JVMを構成するように心がけてください。Full GC が 5~10分毎、Scavenge GC は 5~10秒毎に発生しているようであれば正常な JVMの振る舞いを示しています。

2-4-2 Javaヒープ・メモリの構造 このセクションではヒープ管理の詳細へと話を進めていきます。JVMのヒープ・メモリ全体の中で、新しいオブジェクトと古いオブジェクトがどのように

配置されるかを理解することで、そのスペースがいかに有効に利用されている

Page 17: HP-UX Java パフォーマンス・チューニングガイド

18/18

か、あるいはその逆であるかを判断することができます。JVM オプション –Xms、-Xmx、-Xmn を使うことによって、こうした振る舞いを変更することができます。 図 6は JVMにおけるヒープ・メモリのレイアウトを示したものです。

この図のように、ヒープ・メモリの中には 4つのスペース、あるいは活動領域があります。この世代別に分けられたガベージ・コレクションの設計目的は、

短命のオブジェクトに対しては ”NEW” 活動領域でその一生を過ごし、高速なアルゴリズムによってガベージ・コレクションされるべきであるということと、

長い間存在するオブジェクトに対しては “OLD” 活動領域に移ってもらい、低速なアルゴリズムによって発見され取り除かれるまでそこに留まるべきであると

いうことです。

ヒープ・スペースには他にも”Permanent”領域があり、ここにはロードされたクラスの情報が置かれます。Permanent 領域のサイズは以下のオプションで指定できます。

-XX:PermSize permanent 領域の初期サイズ

-XX:MaxPermSize permanent 領域の最大サイズ

Permanent 領域も含めたヒープ・メモリのレイアウトを図 7 に示します。

NEW (16/2 MB) OLD (48/4 MB)

Eden To Tenured Objects

-Xmn -Xmx

Eden From SurvivorRatio = Eden/From = 8

MaxTenuringThreshold (32)

From

図 6 ヒープ・メモリのレイアウト

NEW

(MAX 16MB)

図 7 Permanent 領域を含めたヒープレイアウト

Permanent (MAX 64MB)

OLD (MAX 48MB)

Eden Tenured Objects From Reflection Object

-XX:MaxPermSize

To

Page 18: HP-UX Java パフォーマンス・チューニングガイド

19/19

Permanent領域の詳細については(付録 B) “HP-UX HotSpot JVMのPermanent 領域について”を参照してください。

Scavenge GC は “NEW” 活動領域内で行われ、高速であることを目的としています。Full GC は “NEW” と “OLD” の両方の領域をカバーし、そのため Scavenge GC よりは遅くなります。 “NEW” 領域のデフォルトのスタート時のサイズは 2MB、デフォルトの最大サイズは 16 MB です。 “OLD” 領域のデフォルトのスタート時のサイズは 4 MB、デフォルトの最大サイズは 48 MB です。これらの値は以下の JVMオプションを用いて変更できます。

-Xms スタート時のヒープ・サイズ

-Xmx 最大ヒープ・サイズ

-Xmn “NEW” 領域のサイズ

-XX:SurvivorRatio=<n> “Eden” エリア・サイズを”From” あるいは “To” エリアのサイズで割った数。”From” と “To” のサイズは同じ。

最初の 3つのオプションについては、割り当てる領域のサイズをメガバイト単位の数字で指定します。例えば –Xmn256m は”NEW” 領域に 256 MB のサイズ割り当てを指定します。 一つの推奨すべき方法は、-Xmn の値を –Xmx の 1/3 に設定することです。より積極的にチューニングすべき状況では、-Xmn の値を –Xmx の半分に指定することも可能です。 “NEW” 領域内の “Eden” のスペースは、新しいオブジェクトが作成された際に最初に配置されるメモリ領域です。これは非常に重要なスペースです。という

のも全てのユーザー定義オブジェクトはプログラム実行中の同じ時刻に作成さ

れるからです。そしてそれらは長い時間存在することもありますし、そうでな

い場合もあります。一つの例として Panelという一般的なユーザーインターフェイス・クラスのオブジェクトを Javaプログラムが作成した場合を考えてみましょう。

Panel p = new Panel();

この新しいオブジェクトは 他の場所にコピーされるまで “Eden” スペースに存在します。このオブジェクトがプログラムの中で長い間使用される場合は、”OLD” 領域に移動されます。

2-4-3 Java ガベージ・コレクションのメカニズム このセクションでは Javaガベージ・コレクションの概要を説明します。図 8に示すように、通常 ”Eden” スペースは新しいオブジェクトが作成されるに従い、

NEW (MAX 16MB)

Page 19: HP-UX Java パフォーマンス・チューニングガイド

20/20

時間とともにどんどん埋まっていきます。”Eden” が一杯になると、Scavenge GC が開始され、リファレンスが存在するオブジェクトは “Eden” スペースから “To” スペースにコピーされます(図 9)。これは “To” スペースへの最初の移動なので図中には “1” と表記しています。

“To” スペースは 2つある“survivor” スペースのうちの 1つであり、使用されているオブジェクトを一定の期間保持し、GC イベントを数回 “survive” するまで ”OLD” 領域へ移動させられるのを遅らせます。もう1つの “survivor” スペースは “From” です。 リファレンスを持たないオブジェクトはこの時点で ”Eden” スペースより取り除かれ、以前占有していたスペースは利用可能になります(図 9)。このようにして不要なオブジェクトがガベージ・コレクションにより取り除かれます。

MaxTenuringThreshold=2

Eden From To

New (16/2MB) Old (48/4MB)

図 9 Scavenge GC(移動)

1 1 1 1

MaxTenuringThreshold=2

Eden To To

New (16/2MB) Old (48/4MB)

図 10 Scavenge GC(完了)

1 1 1 1

MaxTenuringThreshold=2

Ed From To

New (16/2MB) Old (48/4MB)

図 8 新しいオブジェクトは Eden に

Page 20: HP-UX Java パフォーマンス・チューニングガイド

21/21

Scavenge GC 完了後、”From” スペースは ”To” スペースへ、”To” スペースは ”From” スペースへと名前が交換されます。 時間が経過し、オブジェクトが作成されるに従い、再び “Eden” が一杯になり、図 11に示すように 2度目の Scavenge GCが発生します。”Eden” スペース、および ”From” スペースにあるリファレンスのあるオブジェクトは、”To” スペースへと移動されます。”To” スペースへの移動が 2度目のオブジェクトは図中で “2” と記しています。図 12 は 2 回目の Scavenge GC が完了した後の状態です。

このようにオブジェクトはそれらが参照されている間、Scavenge GCが発生する度に ”From”スペースと ”To” スペースの間でコピーを繰り返します。これは “MaxTenuringThreshold” で示されるコピー回数の限界まで続けられます。図 12には説明を簡単にするために “MaxTenuringThreshold = 2” としていますが、デフォルトは 32 です。従って、オブジェクトが “NEW” 領域において最大 32回の Scavenge GC( = 32回の From-To 名前変換)まで “survive” することを意味します。それ以降はオブジェクトは ”tenured” となり、”OLD” 領域へと配置されます。この “OLD” エリアはプログラム実行中、長い期間存在するオブジェクトのためのものです。 3度目の Scavenge GCが発生すると、“From” 内のオブジェクトは再度 “To”スペースへと移動することになりますが、3度目の移動となるオブジェクトに関しては、この例で設定されている ”MaxTenuringThreshold = 2” を超えるため、”To” スペースではなく、”Old” スペースへとコピーされることになります。この様子を図 13に示します。

MaxTenuringThreshold=2

Eden From To

New (16/2MB) Old (48/4MB)

図 11 2 回目の Scavenge GC(移動)

To 2 2 1 1 1 1

MaxTenuringThreshold=2

Eden To

New (16/2MB) Old (48/4MB)

図 12 2回目の Scavenge GC(完了)

To 2 2 1 1

Page 21: HP-UX Java パフォーマンス・チューニングガイド

22/22

eden: 1834928->0/3670016

以上の説明により –Xverbosegc の出力をより理解することができるようになりました。出力ファイル中の上記のようなエントリはそれぞれ、現在の GCイベント前に占有されていた “Eden” スペースのサイズ(矢印の左側の数字)、イベント後の “Eden” 内の使用されているサイズ(矢印の右側の数字)、”Eden” 全体のサイズ(”/” の右側の数字)です。よってこのエントリは以下のように解釈されます。

► この GCが発生する前は、”Eden” スペースを 新しいオブジェクトが 1834928 バイト使用していた。

► GC後の ”Eden” の使用量が 0(矢印の右側)なので、この GCによりこれらのオブジェクトは ”Eden” スペースより完全に取り除かれた(そしてどこかに配置された)。

► GCが終了した後、”Eden” の合計サイズは 3670016 バイト。 同じ手法が “survivor” と “old” にも適用できます。この場合、”survivor” という用語は “From” と “To” を合わせたものとして使われます。 Scavenge GC が起こるたびに “Eden” スペースが一掃されるのはガベージ・コレクションにおいて通常の振る舞いです。しかし、もし “Survivor” (“From” と “To”) の数値が Scavenge GC後に 0になるようならば、何かおかしなことが起こっています。これはオブジェクトが十分に “From” と “To” の間を行き来せず、”Old” スペースにすぐに移されているということです。こうしたアクションが繰り返されると、”Old” スペースは短命オブジェクトによりすぐに一杯になってしまいます。この症状はオーバーフローと呼ばれ、MaxTenuringThreshold が低い(2~10)ために起こる GCの連続した発生によって見つけることができます。 オブジェクトを適切な期間、”New” エリアに保持する十分なスペースがない場合は、まず –Xmn オプションを用いて ”New” エリアの大きさを調節することができます。これにより、全ての “New” スペース(”Eden”、”from”、”To”)のサイズが拡張されます。以下のように用います。

MaxTenuringThreshold=2

Eden To

New (16/2MB) Old (48/4MB)

図 13 3回目の Scavenge GC(移動)

From 3

3 2 2 1 1 1

Page 22: HP-UX Java パフォーマンス・チューニングガイド

23/23

$ java –Xmn160m –Xmx480m –Xms480m ClassName

最大ヒープ・サイズ全体(-Xmxで指定)に対する ”New” エリア・サイズの推奨比は 1/3 から半分です。半分の場合はかなり積極的なチューニングとなります。 “Eden” のサイズに対する比率として “From” と “To” のサイズを調節することもできます。以下のように JVMオプションの –XX:SurvivorRatio=<n> を使用します。

$ java –Xmn120m –Xmx480m –Xms480m –XX:SurvivorRatio=8 ClassName

これにより”Eden” のサイズは “From” あるいは “To” のサイズの 8倍となります(”From” と “To” のサイズは同じです)。上記の例の場合、”New” 領域は96MB(8×12MB)の “Eden” スペースと、それぞれ 12MBの “From” と “To” に分割され、これらの合計は 120MBとなります。 -XX:SurvivorRatio の値はデフォルトのままにしておくことが推奨されています。この値は JDK 1.3.1 では 8です。 これらの対策は、Full GCの回数を減らすこととMaxTenuringThreshhold をできるだけ 32に近づけるという観点に基づいて行われるべきです。このMaxTenuringThreshholdの値はガベージ・コレクションが進行するに従い調整されるため、プログラム実行中は常に変化する可能性があります。低い値になる

ほど、オブジェクトは “From” や “To” スペースから “Old” 領域へ移りやすくなります。これは避けるべき状況です。 JVMオプション –Xverbosegc を指定し長い間実行すると非常にたくさんのデータが出力されるので、MS Excelなどの表計算ソフトに –Xverbosegc の出力を取り込むのも 1つのテクニックです。表計算ソフト上で時間の経過とともに ”New”、”Old” 領域が増減する様子をグラフに描きます。この方法については以下の Developer Solution Partner(DSPP)webサイトに詳しく書かれています。 http://www.hp.com/dspp 同様に、-Xverbosegcの出力をグラフ化するためには、MS Excelを利用する以外にも、現時点ではベータバージョンですが javaベースのツール HPjtuneを使用することもできます。 http://www.hp.com/products1/unix/java/java2/hpjtune/index.html ここでは簡単な例として Javaのベンチマークプログラムの 1つである SPEC JBB2000でのヒープ・サイズの大きさの変更に伴う GC後の ”Old” 領域の変化を見てみます。 図 14はヒープ・サイズの調節なしに SPEC JBB2000を実行した結果です。

Page 23: HP-UX Java パフォーマンス・チューニングガイド

24/24

GC が頻発し、Old 領域の急激な増加がみられます。これは New サイズが小さいためオーバーフローが起こり、Old 領域に Live Objectが溢れている望ましくない状態と考えられます。また Full GC の実行のたびにOld 領域のサイズが不連続に大幅に減っていることが観察されます。このことから長い間存続するこ

とのない Live Objectまでもが Old 領域に移動してしまっていると推測できます( Full GC後のOld領域が定常的に増加しているのは実行プログラムであるSPEC JBB 2000の特性によるものです)。

図 15は JVMのオプションによりヒープの調節を行った後のものです。図 14の場合と比較し、GCの回数も減少し、Old領域の急激な増加もみられません。これはヒープの調節 (New領域、Old領域の拡大、SurvivorRatioの変更等) を行った結果、望ましい状態になったと考えられます ( Old領域が段階的に増加しているのは実行プログラムである SPEC JBB 2000の特性によるものです)。

000.0E+0

50.0E+6

100.0E+6

150.0E+6

200.0E+6

250.0E+6

0 200 400 600 800 1000 1200 1400

Time (seconds)

Live

Obj

ects

(byt

es)

図 14 SPEC JBB2000 における GC後の Old 領域のサイズ変化 (ヒープ・サイズ調節前)

Page 24: HP-UX Java パフォーマンス・チューニングガイド

25/25

2-5

リソース競合

Javaはプログラム内で簡単にスレッドや非同期プロシージャを利用できるプログラミング言語です。これは言わば諸刃の剣であり、マルチ・スレッド・プロ

グラミングに習熟していないプログラマの場合には問題が発生することが多々

あります。 スレッドを無制限に作成するのはもちろん悪いやり方であり、行ってはいけま

せん。多くのアプリケーション・サーバー(ミドルウェア・システム)では数

千のスレッドを作成します。hp-uxは 1つのプロセス内に複数のスレッドをサポートしますが、このスレッド数はカーネル・パラメータ max_thread_proc によって制限されます。この値は以下の “kmtune” コマンドを実行することで確認することができます。

$ kmtune | grep max_thread_proc

幸運にも、これらのスレッドの振る舞いを実行中、あるいは実行後に観察する

ことのできる各種のツール(Glance/gpm、HPjmeter、”kill –3”を使うテクニック)が提供されています。これらについては後ほど詳述します。

000.0E+0

50.0E+6

100.0E+6

150.0E+6

200.0E+6

250.0E+6

0 200 400 600 800 1000 1200 1400

Time (seconds)

Live

Obj

ects

(byt

es)

図 15 SPEC JBB2000 における GC 後の Old 領域のサイズ変化 (ヒープ・サイズ調節後)

Page 25: HP-UX Java パフォーマンス・チューニングガイド

26/26

図 16は特定の Javaプロセスを Glance/gpmを使って表示し、Javaプログラムが実行中に生成した全てのスレッドを考察している図です。画面の一番左の TIDはそれぞれのスレッドに対するスレッド IDです(このスレッド IDは、後述するスレッド・スタック・トレースの lwp_idに対応します)。 JVM自身を開始するために 11のスレッドが必要だということを覚えておいてください。これらのスレッドはユーザー・プログラム・コードで必要となるスレ

ッドとは別のものです。 Javaプログラマがスレッドを使う上で気をつけなければならないことを以下にまとめます。

► オペレーティング・システムによって決められている制限を越えてスレッドを作成してはならない。 Javaプログラム中で ”OutOfMemoryException” エラー、あるいはスレッドが多すぎるというメッセージを受け取ります。この問題の

解決は簡単です。HPjconfig ツールを使い、カーネル・パラメータ “max_thread_proc” の値を確認し、SAM(管理ツール)を使って値を変更してください。 HPjconfig は HP Java webサイトより無償でダウンロードできます。 http:/www.hp.com/go/java

Active Threads

JVM Threads

Total Number of Threads Thread ID

図 16 Javaプログラム中のスレッド

Page 26: HP-UX Java パフォーマンス・チューニングガイド

27/27

► あるスレッドが、共通のリソースに対するロックを長い間保持することにより、他のいくつか、あるいは全てのスレッドの処理が

できない状態は避けなければならない。 図 17はあるスレッドがリソースを保持しているため、他のスレッドの進行が止まっている問題を示しています。

ロックの競合は、どのくらいのスレッドがロックを獲得しようとしているか、

どのくらいの頻度でそれを行っているかによって決まります。競合が激しい場

合は、有意義な作業を行う代わりに、オブジェクト・モニタに入るのを待つこ

とにスレッドの時間が費やされます。 hp-ux 上の JDK 1.3.1(あるいはこれ以降)では、-Xeprof オプションを使用して、ロック競合に関する正確なデータを得ることができます。このデータは HPjmeter によって解釈されます。 あるスレッド T1がリソース Aのロックを保持しています。リソース Aはスレッド T2で必要となるリソースです。そしてスレッド T2はリソース Bのロックを保持しています。このリソースはスレッド T1が必要としているリソースです。この場合、これらのスレッドは抜け出せない状況に陥り、どちらかのスレッド

がロックを開放しない限り永久に進展はありません。 これらの問題は全てアプリケーション・ソフトウェアの設計に起因しています。

それゆえソフトウェアの構造を変更すること(再コンパイルを含む)により大

部分の問題は解決されます。

SharedResource

thread 1

thread 2

thread 3

Monitor

SharedResource

thread 1

thread 3

thread 2

Control access to shared resources

Java monitors control access

図 17 複数のスレッドによりアクセスされるリソースの制御

Page 27: HP-UX Java パフォーマンス・チューニングガイド

28/28

スレッドやロック競合の問題に対する最初の手掛かりは Glance/gpmの出力より得られます。図 18は、ほとんどの CPU時間が ”User” タイム(明るい色)ではなく ”System”タイム(暗い色)で消費されているシステムを示しています。正常なシステムはこの逆でなければなりません。”User” タイムが CPU時間の大半を占めるべきです。この時間がアプリケーション・コードの実行に費やされ

る時間です。

これはスレッド、あるいはロック競合の問題が起こっていることを示すひとつ

の指標となります。ここで再び Glance/gpmを使い、システム上の個々の JVMプロセスによって呼ばれているシステム・コールを見てみます。 ロック競合問題を抱えているプロセスでは図 19のような画面が表示されます。Glance/gpm の “Cumulative system call count” 欄が示すように、”sched_yield” や ”ksleep”、”kwakeup” などの特定の hp-ux オペレーティング・システム・コールが他のコールと比べてかなり多くの回数呼ばれています。これらのコールは

JVMのバージョンによって異なる場合もありますが、呼び出し頻度の高いコールとして覚えておくべきものです。図 19において ”send” と “recv” コールの呼び出し頻度を指している矢印は、これらのコールが “sched_yield” や “ksleep” と比べてかなり低い頻度で呼ばれていることを示しています。”send” や “recv” コールはネットワーク・プログラムにおいて主要な作業を行っているので、これ

らのコールはもっと高い頻度で呼ばれるべきです。 もしここで完全にスレッド競合の問題を解決することができたなら(例えばポ

ーリング・スレッドやスレッド・プール・モデルなどでスレッド設計を再構築

するなど)、”send” や “recv” コールはもっと煩雑に呼ばれることになるでしょう。

Low User CPU Time

High System CPU Time

図 18 リソース競合が発生している証拠

Page 28: HP-UX Java パフォーマンス・チューニングガイド

29/29

このような問題は、ネットワーク・トラフィックをオープンするためのソケッ

トそれぞれが1つのスレッドとして使用されているプロジェクトで発生しまし

た。数百、数千のネットワーク・コネクションが同時に確立している状況では、

スレッド競合によるかなり高いオーバーヘッドが見られることになります。こ

のプロジェクトにおける問題は HP Poll APIを使ってインプリメントすることで修正されました。Poll APIは少ないスレッドを使用するアプリケーション設計のひとつのパターンです。この内容については本書の範囲を超えるので省略しま

す。Java SDK 1.4 では非同期 I/O環境においてスレッドの制御が容易になるサブシステムが実装される予定です。

2-5-1 スレッド・スタック・トレース kill –3 JVM でスレッドを分析するこの方法は非常にシンプルでしかも非常に強力です。

動作している Javaプロセスに対して、パラメータ “-3” あるいは “-s SIGQUIT” を用いて “kill” コマンドを実行すると、JVMの実行には影響を与えずに、その瞬間に存在しているスレッドの全ての情報を詳細なダンプとして得ることができ

ます。Glace/gpm を使用するか、あるいはシンプルに、

# ps –ef | grep java

とコマンドを打つことで対象とする JVMプロセスを特定することができます。そして以下のコマンドを実行することでこのプログラムのスレッド・データの

フルダンプを標準出力に出力します。

# kill –3 3493 (ここで 3493 は JVM の PID)

標準出力をファイルに保存するには、JVM起動時にリダイレクトを行います。上記の “kill” コマンドはスーパーユーザーの権限が必要なこともあります。JVM

Pattern for contention: HIGH sched_yield() and ksleep()

図 19 スレッド競合を示す高いシステム・コール呼び出し数

Page 29: HP-UX Java パフォーマンス・チューニングガイド

30/30

によって生成されるデータ(サイズが大きい場合にはファイルにリダイレクト

する必要があるかもしれません)は次の例のようなものです。

"Worker Thread 17" prio=9 tid=0x1310b70 nid=41 lwp_id=14165 suspended [0x1194d000..0x11948478]

at fields.FieldPropertiesLibraryLoader.forClass(FieldPropertiesLibraryLoader.java:67)

- waiting to lock <0x3ca45848> (a java.lang.Object) at fields.FieldsServiceImpl.getFpl(FieldsServiceImpl.java:75) at fields.FieldsServiceImpl.getFpl(FieldsServiceImpl.java:64) at base.core.BaseObject.getFpl(BaseObject.java:2930) at

base.core.BaseObject.getFieldProperties(BaseObject.java:2661) at core.BaseObject.getFieldProperties(BaseObject.java:2670) at

fields.FieldProperties.getFieldsInGroup(FieldProperties.java:1157) at

fields.FieldProperties.getFieldsInGroup(FieldProperties.java:1107)

上の例は、ある特定のスレッド(lwp_id —lightweight process identity--- が14165) がサスペンドの状態にあり、オブジェクト(16進数アドレスで特定)のロックを獲得しようと待っている様子を示しています。これは大きなダンプ

のほんの一部のスナップショットですが、興味深い情報を提示しています。こ

の出力ファイル中で探そうとしているのは、他のスレッドが必要としているオ

ブジェクトのロックを長期間保持しているスレッドです。これにより JVM全体が著しくスローダウンし、深刻な問題となります。(Javaスタックトレースによるリソース競合検出の詳細については付録 Dを参照してください。) 問題のあるプログラム実行中は何回でも “kill –3 <JVM プロセス ID>” を繰り返すことができます。しかし多数のスレッドを含むプログラムを対象とする場合に

は、莫大な量のデータが生成されることもあります。問題を探すためにこのデ

ータを綿密にチェックすることは非常に大変です。こうした作業を簡素化する

ために、次に述べる HPjmeterツールが使用可能です。

2-5-2 スレッド・ロック問題 – HPjmeterの使用 JVMのスレッドの振る舞いを分析するシンプルなアプローチは HP特有のオプションである –Xeprofを指定してプログラムを最後まで走らせ、その後その出力をHPjmeterツールを使用して解析することです。 JVM上で動作するプログラムが正常な方法で終了できる場合は(つまりSystem.exit()を使うか、他の段階的なシャットダウン手法によって)、以下のような拡張プロファイリング・オプションを指定して JVMを起動してください。

$ java –Xeprof:file=myfile.eprof ClassName

ここで出力されたファイル(例では myfile.eprof)を分析するためのツールがHPjmeterプロファイリング・ツールです。このツールは以下よりダウンロード可能です。 http://www.hpjmeter.com

Page 30: HP-UX Java パフォーマンス・チューニングガイド

31/31

このツール自体が Javaで書かれており、JVMがサポートされているどのプラットフォームでも実行することができます。このツールは–Xrunhprofによって出力されたファイルを読むこともできますが、HPの JVM特有のオプションである –Xeprof からの出力ファイルを利用することにより多くのパフォーマンスに関する情報を得ることができます。 HPjmeterを起動するコマンドは以下に示されているとおりです。

$ java –cp /opt/hpjmeter/HPjmeter.jar HPjmeter

この無償のツールはスレッド生存期間に関するものだけでなく、多くのメトリ

ックを備えています。これらのメトリックは、ダウンロード・サイトのチュー

トリアルに詳細が記述されています。このセクションではスレッドの分析に焦

点を絞ります。HPjmeterは、JVM実行中のスレッドの状態と生存期間をグラフィカルなイメージとして表示します。-Xeprof の出力をツールに取り込み、”Metric” メニューの Thread Histogram を選ぶと図 20のような画面を見ることができます。

この画面で一度に全てのスレッドの生存期間を確認することができます。それ

ぞれのスレッドを表すバーをダブル・クリックすることで、問題のある 1つのスレッド、あるいはスレッド・グループを更に分析することができます。図中

のスレッド 0はその存在期間のうち 77.1%を CPUに費やしています。これは正常な証拠です。このスレッド時間の残りのほとんどである 21.4%はプロファイラーのオーバーヘッドに使われていると報告されており、実行待ちやロック競

合で使われている時間はほとんどないことがわかります。 HPの Java SDK 1.3.1以降と HPjmeter 1.2以降を用いることにより、Javaプログラムのスローダウンの原因となるスレッド競合について極めて正確な情報を得

ることができます。

Sort Threads Histogram

Double-click for Pie Chart

Thread 0

CPU

図 20 HPjmeter によるスレッドの状態と生存期間の表示

Page 31: HP-UX Java パフォーマンス・チューニングガイド

32/32

HPjmeterはスレッドの他にも、多くの場面における分析に威力を発揮します。CPUを多く消費するメソッドや、その実時間などもこのツールを使って追跡することが可能です。これについては以降の「リソースを大量に消費するメソッ

ド・コール」で解説します。

2-5-3 スレッドの優先度 Javaプログラム・スレッドは Javaバーチャル・マシンによって hp-uxライトウエイト・プロセス(LWP)と呼ばれるオペレーティング・システムのスレッドへとマッピングされます。hp-ux内でスケジューリングされる単位は、個々のスレッドです。それぞれのスレッドは、例えば Glanceツールの ”Thread List” 機能を使えば一意な LWP IDと共に表示させることができます。 最初はそれぞれのスレッドの優先度は、オペレーティング・システム上の全て

のユーザー・プロセスで共通の値が選ばれます。スレッドが CPUで実行されるに従い、正常な状態では CPU時間が割り当てられるたびにその優先度は低くなっていきます。オペレーティング・システムは優先度を基にスケジューリング

を決定するので、このスレッドは次に他のスレッドと比較される際に不利とな

ります。 ある特定の Javaプロセスがそのコンピュータ上で唯一の重要なユーザー・プロセスである場合は、スーパーユーザーによって起動時、あるいは実行中により

高い優先度を与えることも可能です。これは “nice”、”renice” コマンドを使うか、Glance/gpmの ”renice” 機能を使って行うことができます。 nice コマンドの使用例を以下に示します。

# nice -–20 java ClassName (”-“記号が 2 つあることに注意)

renice コマンドでは、”ps –ef” コマンドや Glance/gpmの Process list画面によって得られる Javaプログラムのプロセス IDが使用されます。次の例で使われている Javaプロセス IDは 1234です。

# renice –20 1234 (”-“記号は 1 つ)

nice および reniceコマンドを実行する際は十分に注意してください。これらのコマンドはスーパーユーザーでログインしている間のみ使用できます。またこ

れらのコマンドは優先度が変更されたプロセス内の全てのスレッドの優先度を

変更します。 上記のようなコマンドを実行することにより、同じコンピュータ上の他のプロ

セスは相対的に優先度が低くなり、以前と比べて CPU時間の割り当てが少なくなります。これらはトレードオフの関係であり、特定の環境では有用な方法で

す。

2-6

リソースを大量に

Page 32: HP-UX Java パフォーマンス・チューニングガイド

33/33

消費するメソッド

・コール

Javaプログラムでは時々、少ない数のプログラム・メソッドが、そのプログラムのほとんどの時間とリソースを消費する場合があります。アプリケーション

設計において、これは明らかにチューニングの対象となるものです。 特定のメソッドには、アプリケーションのデザインやソースコードを変更する

ことなくチューニング可能なものがあります。まず最初のステップはこれらの

メソッドを見つけ出し、そのパフォーマンス特性を把握することです。HPjmeterはこうした分析に最適なツールです。 HPjmeterには Javaが動作可能な環境全てにおいて無償で利用できるという特筆すべき利点があります。更に HPjmeterに必要となるデータを生成する JVMオプション –Xeprof は、プロファイリングを行うプログラムへの影響ができるだけ小さくなるように設計されています。

HPjmeterの起動方法は以前に示したとおりです。図 21は HPjmeterにおいて呼び出し回数が最も多いメソッドを表示させた画面です。 この図より、このプログラムで呼び出されているメソッドの中で最も回数が多

いのがMark.SimSearch.bel()メソッドであることが明らかとなります。 ”bel()” メソッドは呼び出し回数で他のメソッドを大きく引き離しています。HPjmeterの他のメトリック、例えば “Exclusive CPU Method Time” などを使用することにより、CPUサイクルや他のプログラム・リソースを実際どれくらい消費しているかを知ることもできます (図 22)。その後、このボトルネックを取り除くために、このメソッドの設計や使用方法を変更します。

図 21 HPjmeter による Javaプログラムのメソッド呼び出し 回数の表示

Page 33: HP-UX Java パフォーマンス・チューニングガイド

34/34

ある特定のケース、特に Javaプログラムで Dateクラスのオブジェクトが絡んでいる場合は、ソースコードを書き換えることなくメソッドをより高速にできる

可能性があります。Javaプログラムの中で Dateオブジェクトが非常に多く使われている場合、メソッド “currentTimeMillis”、あるいは “getTimeOfDay” がHPjmeterのメソッド呼び出し回数の上位に見られるはずです。 Dateオブジェクトは JDBCを使用してデータベースからレコードを読み込んだり、データベースへ書き込んだりする場合に煩雑に使用されることがあります。 これがプログラムのパフォーマンスに深刻な弊害をもたらしていることがはっ

きりした場合には、問題を解決するために次のオプションを使用することがで

きます。このオプションは、プログラムを実行している JVMより呼ばれているdate/time関数(OS内ではアトミックな処理)の影響を小さくする効果を持ちます。

$ java –XX:-UseHighResolutionTimer ClassName

2-7

メモリ・リーク

Java開発において最も注意すべきことのひとつは、メモリ不足によってプログラムが停止する可能性です。これは “java.lang.OutOfMemoryException” などのエラー・メッセージにより顕著になることもありますが、プログラムの停止と

いうあまりユーザーに親切ではない形で表れることもあります。 Javaを使用しているプログラマは、ガベージ・コレクションのセクションで解説したような方法でオブジェクトにメモリを割り当てます。理論上は、オブジ

ェクトが使われなくなるとそのメモリは自動的に開放され、プログラマが心配

することはありません。

あああああ

図 22 HPjmeter による Javaプログラムのメソッド CPU 時間の表示

Page 34: HP-UX Java パフォーマンス・チューニングガイド

35/35

しかし、オブジェクトに対する全てのリファレンスが取り除かれない限り

(”null” のセット、あるいはスコープ外)、JVMはそのオブジェクトが再びプログラム実行中に使用されると思い込みます。これはメモリ・リテンション問題

と呼ばれています。C++ などの他の言語でのメモリ・リークとは概念的に大きく異なります。 C++ でのメモリ・リークは以下に示すようなパターンに従ったコードによって起こります。 (プログラムの一部)

ptr = new LargeObjectType(); // where ptr is a pointer of a correct type

// The object is used for some purpose, then it is finished with. ptr = null; // The programmer has not used “delete ptr” to free space before

setting ptr to null

Javaでは問題は異なり、オブジェクトに “null” リファレンスをセットすることはプログラムがそのオブジェクトの使用を終えたことを意味するのでこれは良

い作法となります。更に Javaプログラマは、”free” や “delete” などのオペレータがないのでオブジェクトに割り当てられたメモリを明示的に開放する手段を

持ちません。実際、Javaではメモリ・リテンション問題は、オブジェクト使用後にプログラマがリファレンスに “null” をセットすることを忘れることに起因しています。 もしこの ”null” をセットし忘れたオブジェクトが他のオブジェクトを連鎖的に参照している大きなオブジェクトであった場合、これらのオブジェクトに占有

されているメモリ領域は、それらがスコープ内にいる間は利用不可能になりま

す。長い間プログラムが実行され、こうした意図しないオブジェクトの振る舞

いが繰り返されることで、プログラムのメモリが使い果たされます。これは

JVMを予期しないときに突然終了させる原因となります。 Javaのメモリ・リテンション問題は、こうした腹立たしいコードを修正すること以外には一般的に解決することはできません。この修正については本ドキュ

メントでは触れません。Javaプログラマはこれらの問題を早期に、開発段階での発見を目指して HPjmteterや JProbeプロファイラ・ツール(Sitraka Corporation)が有効です。HPjmeterによるメモリ・リテンションについては次のセクションで述べます。JProbeの詳細については以下のサイトを参照してください。 http://www.sitraka.com メモリ・リークやメモリ・リテンションを発見することは初心者にとっては難

しい問題です。この目的には、Glance/gpmツールの “Memory Regions” 画面が大変役に立ちます。対象となるプロセス(Glance/gpmでは JVMは通常 “java”という名前でリストに表示されます)を選び、そのプロセスで使用されている

全てのメモリ範囲のレポートを表示させてください。これにより図 23のような画面が表示されます。

Page 35: HP-UX Java パフォーマンス・チューニングガイド

36/36

図 23では、Data RSS(Resident set size)と Data VSS(Virtual set size)の値がCプログラム・ヒープへのメモリ割り当てを示しています。Other RSSとOther VSS、そして Private RSSサイズはこのプログラムで Javaヒープに割り当てられているメモリのサイズを意味します。これらの領域のうちひとつにでも継続的

な増加が見られる場合は、この Javaプログラムでメモリ・リテンション問題が存在すると考えてまず間違いありません。

多くの Javaアプリケーションは背後で C/C++プログラムを呼び出しています。メモリ・リークやメモリ・リテンション問題が Javaプログラムの裏に潜んだC/C++プログラムに起因しているということはよくあることです。 このように、この問題の性質上、Data RSSや VSSサイズの振る舞いを分析することが重要となってきます。 対象となるプログラムで現象が発生するまでに長い時間がかかる場合には、上

記のように画面を監視し続けるのはほぼ不可能かもしれません。その場合には、

Glance/gpmツールキットの ”adviser mode” が便利でしょう。これはこのツールのバッチ・モードであり、集めたデータをファイルに出力し、後ほどの分析

に役立てることができます。 以下のコマンド例では、Glance/gpmは 5秒毎にサンプルを採り、adviser_commandsファイル中に書かれたコマンドを使用します。

$ /opt/perf/bin/glance –adviser_only –syntax adviser_commands –j 5

adviser_commandsファイルには以下のような書式が用いられます。

C Heap

Java Heap

図 23 プロセスが使用しているメモリの表示

Page 36: HP-UX Java パフォーマンス・チューニングガイド

37/37

PROCESS LOOP { if proc_proc_name == "Java" then { PRINT proc_proc_name|8|0,

proc_proc_id|8|0, proc_cpu_total_util|8|2, proc_cpu_nnice_time|8|2, proc_mem_virt, proc_mem_res, proc_thread_count, proc_io_byte_rate

} }

以下は、Glanceを adviser_modeで走らせた際の出力例です。

----- 20:15:37 (proc name, pid, cpu, negnice cpu, VSS, RSS, thread #, I/O)

Swap space utilization: 42% nfile utilization: 6% bin 6390 93.33 0.00 787.9mb 389.7mb 2438 0.0 bin 6370 88.66 0.00 788.9mb 308.3mb 2444 0.0 ----- 20:15:38 (proc name, pid, cpu, negnice cpu, VSS, RSS, thread #,

I/O) Swap space utilization: 42% nfile utilization: 6% bin 6390 80.00 0.00 793.8mb 389.7mb 2482 0.0 bin 6370 74.66 0.00 794.7mb 308.3mb 2488 0.0 […]

メモリ・リークの検出には Glance以外にも、-Xverbosegcのデータ解析、HPjmeter (メニュー[Metric]→[Residual Objects(counts)] 等をチェック)、gdb(gnu debugger) の利用等が有効な場合もあります。

2-8

HPjmeterを使った

メモリ・リテンション

の解析

この節では、HPjmeterを使ってメモリ・リテンションの解析を行う例を紹介します。 HPjmeter でプログラムの終了時に GC を行い残存オブジェクトの様子をみます。プログラムの終了は以下のようにするのがよいでしょう。 System.gc() System.runFinalization() System.gc() System.exit() java実行時のオプションには –Xrunhprof:heap=all,cutoff=0を追加しておきます。 また、ある期間の残存オブジェクトの増減の変化を知りたい場合は、2度の実

行の結果を比較する HPjmeterの Compare機能を利用できます。たとえば、ある5分間の間の残存オブジェクトの変化を知りたい場合は、まず、プログラムを

実行し、対象の期間の前までのプロファイルデータをとります。その後、もう

一度プログラムを対象期間の最後まで(1回目の実行より5分間長く)のプロファイルデータをとることにとります。その2つのプロファイルデータの差を

Compare機能で比較することにより対象となる5分間の残存オブジェクトの増減の状況を知ることができます。

VSS INCREASING!

Page 37: HP-UX Java パフォーマンス・チューニングガイド

38/38

以下、簡単な例で HPjmeterで1時間実行の結果と2時間実行の結果を比較し、その間の1時間の残存オブジェクトについて解析します。 1. 1時間実行と、2時間実行のプロファイルデータを HPjmeter でロードします。 (メニュー[File]→[Open]でそれぞれのファイルを連続的にオープンします。) 2. メニュー[File]→[Compare]を選択すると 2 つのデータの概要が表示されます。

3.メニュ-[Metric]→[Residual Objects(Count)]を選択します。 ここで Authoriseオブジェクトに注目してみます。 まず後の検索のために Authorizeオブジェクトの行をクリックし、マークしておきます。 メニュー[Edit]→[Mark to Find]

1時間実行 2時間実行

Page 38: HP-UX Java パフォーマンス・チューニングガイド

39/39

4.さきほどマークした Authoriseオブジェクトについて 2時間試行のデータをもとに解析します。 まず、 2時間試行のプロファイルデータをロードします。メニュー[File]→[hprof_2hr.txt]

5.[Metric]→[Reference Graph Tree]を選択します。

クリック

対象期間(1時間実行と2時間実

行の間)で8017ものAuthorise オブジェクトの増加

Page 39: HP-UX Java パフォーマンス・チューニングガイド

40/40

6.先ほどの Authorizeオブジェクトの参照を確認するため検索を行います。 (メニュー[Edit]→[Find]を選択します) 検索結果を一度に全て表示するか、どれか1つを表示するかを選択するように

ダイアログが現れますが、今回は数が多いのでどれか1つ[Find Any]を選択します。するとある参照テーブルの 7117番目の要素として見つかりました。

Page 40: HP-UX Java パフォーマンス・チューニングガイド

41/41

7.テーブルの参照もとについて確認するために、同じテーブル1番目の要素について参照を確認します。1番目の要素のツリーを展開すると、Authoriseオブジェクトへの参照があることが確認できます。 また要素1のすべてのツリーを展開することにより他への参照なども確認でき

ます。

8. HPjmter 1.2.1からはメモリ・リテンションの疑いがあるオブジェクトを探す機能が追加されました。(この機能では、経験的に参照が1つのみのオブジェクトをメモリ・リテンションの疑いがあると判断しています。これはかならずし

も、期待する結果とはならない場合もありますが、とりかかりとしてはよいで

しょう。) メニュ-[Guess]→[Memory Leaks]を選択します。 (ここではメモリ・リテンションをメモリ・リークと表現しています。) メモリ・リークの疑いがあるオブジェクトのObject IDとバイト数が、表示されます。

Page 41: HP-UX Java パフォーマンス・チューニングガイド

42/42

先ほどの 7.で注目していたオブジェクトと一致していることがわかります。

9. このオブジェクトがメモリ・リテンションの原因の疑いが高いことがわかりましたので、この後はこのオブジェクトに対象を絞ってメモリリークの解析

(プログラム的、論理的な解析も含めて)を行うことになります。 このように HPjmeterを利用してメモリ・リテンションの解析を効率的に行うことができます。

2-8

ベンチマークと

Hot Spot

ベンチマークはしばしばチューニングの効果を比較するために使用されます。

しかし、様々なベンチマークのパフォーマンスを分析してきた経験によると、

特定のベンチマークは誤解を生む原因になる場合があります。例えば、デフォ

ルト JVMである Hot Spotを使って実行しても、使わない場合より遅くなったように見えるなどです。 このセクションでは、Hot Spotコンパイラが何故パフォーマンスを向上させることができないのか、それを改善するためにはどのようにすればよいのかについ

て解説します。 まず以下のプログラムを見てください。このプログラムは、Hot Spot技術の恩恵を全く受けていません。

public class SimpleBenchmark { public static void main(String[] argv) { int value=0;

// Record the start time.

Page 42: HP-UX Java パフォーマンス・チューニングガイド

43/43

long start= System.currentTimeMillis();

// Repeatedly executes feature to measure performance. for (int i=0; i<100000000; i++) { // Replace line with your favorite computation. value +=i;

}

// Record the finish time. long finish= System.currentTimeMillis();

// Now report how long test ran. System.out.print ("Time spent = " + Long.toString(finish - start) + " ms\n");

} }

このプログラムをコンパイルし、Hot Spot JVMを使って実行すると以下のような結果を得ることになります。

$ /opt/java1.3/bin/java SimpleBenchmark Time spent = 24168 ms

試しに同じプログラムを古い JVMで実行し、結果を比較してみます。Hot Spotを JIT(Just-In-Time)コンパイラに切り替えるために –classic オプションを指定します。

$ /opt/java1.3/bin/java –classic SimpleBenchmark Time spent = 911 ms

Hot Spot JVM(動的な最適化)は、JITを使った JVMよりも 25倍も遅いという結果が導かれました。 この結果を更に考察し、コンパイルを一切行わない実行ではどのようになるか

を見てみましょう。hp-ux SDK 1.3バージョンでは、-Xintオプションを指定することで、コンパイルがOFFとなり、単純なバイトコードのインタプリタ形式となり HotSpotインタプリタを呼び出します。

$ /opt/java1.3/bin/java –Xint SimpleBenchmark Time spent = 24100 ms

Just-In-Timeコンパイルを使用することで、インタプリタと比べて約 25倍の高速化となっており、JITコンパイラの威力を証明しています。残念ながらこれら JITによるパフォーマンスの結果は、Hot Spot JVMのものよりも良い結果を示しています。この結果については、クラシック(JIT)と Hot Spotそれぞれが、何をどのような時にコンパイルするのか、また両者の働きがどのように異なってい

るかを理解することで説明できるでしょう。

クラシック インターナル・コンパイラ。しばしば Just-In-Time(JIT)コンパイラとも呼ばれる。繰り返し呼ばれるメソッドや、ループを含む

メソッドの全てをコンパイルする。このコ

ンパイラは最適化はほとんど行わないた

め、インタプリタ方式よりは高速だが、高

度に最適化された Cコードと比べると遅い。

Hot Spot 同じくインターナル・コンパイラ。Java

Page 43: HP-UX Java パフォーマンス・チューニングガイド

44/44

バイトコードの一部を高度に最適化された

マシン語命令に動的に変換する。それぞれ

のアプリケーションに対して最も使用頻度

の高い部分を特定するため、このコンパイ

ラのオーバーヘッドは JITよりも大きい。プログラムの「Hot Spot」が特定された後、これらの領域のコードはコンパイル・

最適化される。

Hot Spotの現在の設計は、長期間実行されるサーバー側アプリケーション向けのものです。ほとんどのアプリケーションは実行時間の大部分を一部の限られた

コードの実行のために費やしています。これは一般的にプログラム・コードの

20%が実行時間の 80%を占めることから、80/20の法則と呼ばれています。Hot Spotは最初アプリケーションをインタプリタ・モードで実行し、「Hot Spot」の分析および最適化を行います。この最適化にはパフォーマンスに大きな影響を与えるメソッドのコンパイルやインライン展開が含まれます。「Hot Spot」が特定され、最適化された後、インタプリタ方式によるバイトコードの実行から、コンパイルされたコードの実行へと処理を切り替えます。長時間実行

されるアプリケーションは、その分コンパイルされたコードを実行する機会も

多いため、Hot Spotの恩恵をより多く受けることができます。しかし、こうしたプログラムは Hot Spotの分析と最適化のために一時的にパフォーマンスに悪影響を与えることにもなります。 先ほどのプログラムではループで多数の繰り返しが実行されているので、Hot Spot によるメソッドのコンパイルが行われます。しかし、ループがメソッド内の一部であり、実行中にメソッドの再呼び出しがないため、通常はコンパイル

されたコードの実行への処理の切り替えが行われず HotSpotの恩恵をうけることがありません。 そのような場合は、-XX:+UseOnStackReplacement オプションを指定して on stack replacement 機能を有効にすることによりパフォーマンスをあげることができます。このオプションを指定することによりメソッド内の一部のループの

繰り返しが実行されている最中でも、コンパイルコードの実行に切り替えが行

われるようになるためです。ただし、この機能を有効にするためには現時点で

はパッチ ( HPUX11.0:PHKL_24943, HP-UX11i PHKL_24751) が必要です。また on stack replacement機能を有効にするのと同時に、コンパイラのセーフポイント機能も有効にする必要があります (-XX:+UseCompilerSafepoints)。詳細は SDK 1.3.1.02以降のリリースノートを参照してください。 このオプションを指定して先のプログラムを実行することにより以下の結果を

得ることになります

$/opt/java1.3/bin/java –XX:+UseCompilerSafepoints -XX:XXUseOnStackReplacement SimpleBenchmark

Time spent = 42 ms

on stack replacement機能を有効にすることにより高速化されていることがわかります。

Page 44: HP-UX Java パフォーマンス・チューニングガイド

45/45

ベンチマークの中で比較的短い時間で終了するものを「マイクロ・ベンチマー

ク」と呼びます。最適化が開始される前にベンチマークが終わってしまう場合、

マイクロ・ベンチマークはパフォーマンスの良い指標とは言えません。こうし

たベンチマークは Hot Spotが想定しているアプリケーションとは異なるものです。実際これらのベンチマークでは、実行時間に加えて分析・最適化にも時間

を費やしているにも関わらず、その効果がほとんど得られないため、パフォー

マンスは悪くなる傾向にあります。結局、短時間しか実行されないアプリケー

ションにとっては、Hot Spot技術はコードをコンパイルする前に分析・最適化を必ず行わなければならないため、パフォーマンスに弊害をもたらすものでしか

ありません。しかし幸いなことにマイクロ・ベンチマークは世の中のアプリケ

ーションを代表するものではありません。 オリジナルのプログラムを少し変更することで、Hot Spotの効果を得ることができます。これは、測定される部分を繰り返し呼ばれるメソッドに移すことで実

現します。コードは以下のようになります。

public class HotSpotBenchmark { public static void runTest() { int value=0;

// Repeatedly executes feature to measure performance. for (int i=0; i<100000000; i++) {

// Replace line with your favorite computation. value +=i;

} }

public static void main(String[] argv) { // Run benchmark multiple times. This will allow us to // see when HotSpot begins executing compiled code.

for (int i = 0; i < 8; i++) {

// Record the start time. long start= System.currentTimeMillis();

// Run benchmark test. runTest();

// Record the finish time. long finish= System.currentTimeMillis();

// Now report how long test ran. System.out.print ("Time spent = " +

Long.toString(finish - start) + " ms\n"); }

} }

このプログラムをコンパイルし、Hot Spot JVMで実行すると以下のような結果を得ることができます。

$ /opt/java1.3/bin/java HotSpotBenchmark Time spent = 23372 ms Time spent = 23400 ms Time spent = 23372 ms Time spent = 11 ms Time spent = 11 ms Time spent = 11 ms Time spent = 11 ms Time spent = 11 ms

最適化されたメソッドを実行する前にインタプリタ形式でそのメソッドを 3回実行していることに注目してください。一般的に Hot Spotがメソッドを特定しコンパイルするのには 1、2分を要します。結果からわかるように、最適な変更

Page 45: HP-UX Java パフォーマンス・チューニングガイド

46/46

を加えることでクラシック、JITコンパイラを大幅に上回るパフォーマンスを得ることができました。80倍以上の高速化です。 ベンチマークを作成する際には、それがアプリケーションの重要な部分を含む

かどうかだけではなく、全体のアーキテクチャを模倣できているかどうかを確

認してください。Hot Spotの効果をあげるためには、そのベンチマークが十分に長い間実行され、「Hot」なメソッドが何回も呼ばれなければなりません。

2-9

その他の JVM

オプション

これまでに出てきた以外の JVMのオプションでパフォーマンスに関連するものをいくつか挙げておきます。(jvmのバージョンによってサポートしているオプションの差異がありますのでご注意下さい。)

-server 長時間稼動するサーバー・アプリケーション

用に調整された Hot Spot VMを実行します(デフォルト)。

-client 短時間稼動する GUIアプリケーション用に調整された Hot Spot VMを実行します。

-Xincgc インクリメンタル・ガベージ・コレクタを有

効にします(デフォルトでは無効)。インク

リメンタル・ガベージ・コレクタは、プログ

ラムの実行中のガベージ・コレクションによ

る一時停止を減少させます。ただし、全体の

パフォーマンスは 10%程度低下します。

-XheapInitialSizes ヒープに関するパラメータ値の一覧を表示し

ます。

-XX:+ForceMmapReserved

lazy swap を行わず、 JVM に設定されているページ・サイズを有効にします。大きなヒ

ープ・サイズを利用するときに、TLB thrashing を防ぐために -XX:+ForceMmapReservedオプションが有効な場合があります。

-Xoptgc 主に短命オブジェクトを扱うアプリケーショ

ンのガベージコレクションを改善します。

-XX:+AggressiveHeap

JVMの限界までメモリを利用します。このオプションは-Xmxや-Xmsといったヒープサイズを指定するオプションと一緒には使えませ

ん。

その他にも FastSwing と呼ばれる機能を使用することによって、リモート Xサーバーで実行される Swingアプリケーションのパフォーマンスが大幅に向上し

Page 46: HP-UX Java パフォーマンス・チューニングガイド

47/47

ます。リモート Xサーバーには、X端末、Exceedや Reflection Xのような PC Xサーバー、およびリモートの UNIXワークステーションが含まれます。この機能を使用するには、次のように指定して javaか appletviewerを実行します。

$ /opt/java1.3/bin/java -Dhp.swing.useFastSwing=true MyApp

または

$ /opt/java1.3/bin/appletviewer –J-Dhp.swing.useFastSwing=true applet.html

この機能はリモート・ディスプレイにのみ使用することをお勧めします。ただ

し現時点では以下のような注意が必要です。 ダブルバッファリングされた Swingコンポーネントは、FastSwing機能が有効になっていると Graphics2D処理を実行できません。この処理を実行すると、次の例外が発生します。

java.lang.ClassCastException: sun.awt.motif.X11OffScreenImage at BezierAnimationPanel.run (BezierAnimationPanel.java:223) at java.lang.Thread.run(Unknown Source)

3 まとめ

このホワイトペーパーで記述されたテクニックとツールは、Javaプログラムのより良いパフォーマンスを達成するための手助けとなります。パフォーマン

ス・チューニングを行う際は、最初に得られた悪い結果に落胆してはいけませ

ん。こうした悪い結果の背後に隠れた問題を解決し、良い結果を得ることは可

能です。 プログラムのパフォーマンスに影響を与えるあらゆるアクションを行う前に、

まずプログラムの振る舞いを観察し、問題を分析してください。これには

Glance/gpm、HPjmeter、ガベージ・コレクションのテクニックなどのツールを使用します。 ここで推奨されているチューニングのサイクルは、測定、分析、ボトルネック

の特定、1箇所の変更、再テストです。このドキュメントでは、HPのツールを使用した初心者のアプローチとして、

► メモリ管理 ► スレッドの振る舞い ► システムと Javaの設定 ► プログラム・リソースの消費

を解説してきました。これらの項目は Javaアプリケーションのパフォーマンス・チューニングに大きく貢献します。

Page 47: HP-UX Java パフォーマンス・チューニングガイド

48/48

4 参考文献

Sauers, R. and Weygant, P., HP-UX Performance Tuning, HP Press

Page 48: HP-UX Java パフォーマンス・チューニングガイド

49/49

(付録 A) ツールを用いた簡単なチューニング例

ここでは HPjmeter,HPjtuneを用いた SPECjbb2000のテスト実行のパフォーマンス問題の解決の過程を簡単な例で示します。 (SEPCjbb2000は Java サーバ アプリケーションのベンチマークとして広く利用されています。SPECjbb2000に関する詳細な仕様や、公式ベンチマーク結果については以下のウェブサイトを参照してください。 http://www.spec.org/osg/jbb2000/)

パフォーマンス

問題例

4-ウェイマシン上で8ウェアハウス(クライアントスレッド数に対応)でのSPECjbb2000の実行結果のグラフを以下に示します。

0

2000

4000

6000

8000

10000

12000

14000

1 2 3 4 5 6 7 8

Warehouse

Scor

e

4ウェアハウスまでスコアは順調に増加していますが、5ウェアハウス以上になると急激にスコアは低下し、8ウェアハウスではピーク時から約40パーセントものスコア低下がみられます。以下では、この問題について Hpjmter, HPjtuneを使って解析を行います。

HPjmeterを使った

解析

SPECjbb2000実行時の Javaオプションに –Xeprof:file=jbb_before.eprofオプションを付けプロファイルデータを jbb_before.eprofという名前のファイルに出力したとします。 1. HPjmeterを起動します。 (例:%java -mx128m HPjmeter)

2.メニューから[File]->[Open]を選択し、ダイアログからプロファイルデータ

があるディレクトリに移動し、jbb_before.eprof を選択しロードします。

Page 49: HP-UX Java パフォーマンス・チューニングガイド

50/50

3.表示されたデータを確認します。

4.メニュ-[Scope]->[Process]を選択したあと、メニュー[Metric]

->[Threads Histgram]を選択します。

Threads Histogram として以下が情報が簡潔に表示されます。

-プログラムの実行中の各スレッドの生存期間

-スレッドの時間がどのように使われたか

5. Thread-0(ウェアハウス 1)をダブルクリックし、パイチャートを表示させ[Detach]ボタンを押して切り離します。

Page 50: HP-UX Java パフォーマンス・チューニングガイド

51/51

6. 同様に、Thread-30を(ウェアハウス 8)をダブルクリッ

クし、[Detach]ボタンをおして切り離します。

7. 2つのパイチャートを比較すると以下のことがいえます。 -Thread-0の CPU時間が 62.4%を占めているのに対し、Thread-30

の CPU時間が 21.0%だけである。 -Thread-0はGarbage Colloction時間はわずかだが、Thread-30は

33.7%も占めている。 -Thrad-0,Thread-30のどちらも Lock Contention時間が

ない。(パフォーマンスの低下の原因は Lock Contentionではないと推測可能)

8. 問題の原因は GC関連の可能性が高いと推測されるので、次にヒープの状態を詳しくみるために HPjtuneを使って解析を行います

Page 51: HP-UX Java パフォーマンス・チューニングガイド

52/52

HPjtuneを使った

解析

SPECjbb2000実行時の javaオプションに –Xverbosegc:file=jbb_before.eproを付けプロファイルデータを jbb_before.vgcという名前のファイルに出力したとします。 1. HPjtuneを起動します。 (例:%java -jar HPjtune.jar) 2. メニューから[File]->[Open]を選択し、ダイアログからプロファイルデータがあるディレクトリに移動し、jbb_before.vgc を選択しロードします。

3.Summaryパネルにデータが表示されるのでチェックします。 一番下ののステータスバーは最も重要な GCに使われた時間を表しています。左から

全 GCに使われた時間の割合(バー表示) フル GCに使われた時間の割合(バー表示) フル GCと全 GCの比較(パイチャート表示)

このサマリパネルから以下のことがい得ます。

-15.55%もの時間が GCに使われている。(“Overall Statics”の”Percentage of time in GC”の欄)

-ヒープの利用率は合計で 99.859%にもなる。(“Heap Capacity”の”Utilization”ラインの”Total”欄

Page 52: HP-UX Java パフォーマンス・チューニングガイド

53/53

4. Heap usageパネルを選択すると、表示内容から以下のことがわかります。

-ヒープの利用が単調増加し、ヒープが一杯状態に向っている。 -実行中に起こっている GCのタイプを見ると、前半は、Scavenge GCと System.gcだが、後半でOld領域が一杯になったことが原因でFull GCが起きている。

5.Durationパネルを選択すると、表示内容から以下のことがわかります。 -後半のある点から、GC時間が急に増加している。 -Full GCにかかる時間は Scavenge GCにかかる時間に比べかなり長い。

Page 53: HP-UX Java パフォーマンス・チューニングガイド

54/54

6.Cumlative パネルを選択すると、表示内容から以下のことがわかります。 -作業量をあらわす Object Allocationが GCに時間がかかりはじめると、その傾きが減少している。

結論 ここまでの解析の結果、パフォーマンスの劣化は GCに関連して起こっているので、問題の原因は、ヒープサイズが十分でないのが

原因と推測できる。

Page 54: HP-UX Java パフォーマンス・チューニングガイド

55/55

チューニング

作業

これまでの HPjmeter,HPjtuneでの解析結果より、ヒープサイズの拡張を行います。このとき HPjtuneの simulation機能と実際のテストを繰り返し、ヒープサイズを適当な大きさまで拡張します。

シミュレーション設定画面

今回は、初期ヒープサイズ 256Mから 512Mに拡張することにより、HPmeter,HPjtuneで以下の変化が観測されました。 Hpjtuneでの確認

--15.2%もの時間が GCに使われている時間が 15.2%から 4.44%に減少。(“Overall Statics”の”Percentage of time in GC”欄) -ヒープの利用率の合計が 99.9%から 59.4%に減少(“Heap Capacity”の”Utilization”ラインの”Total”欄)

Page 55: HP-UX Java パフォーマンス・チューニングガイド

56/56

HPjmeterでの確認

-Thread-30の CPU時間が 21.0%から 31.9%に増加 -Thread-30の GC時間が 33.7%から 5%に減少

チューニング

結果

以下のグラフに示すように、HPjmeter,HPjtuneでの解析をもとに、チューニング(ヒープサイズを 256Mbから 512Mbに拡張)を行ったことによりパフォーマンスが上がっています。(8 warehouseでは約 60%のスコアの上昇)

0

2000

4000

6000

8000

10000

12000

14000

16000

1 2 3 4 5 6 7 8

Warehouse

Scor

e 256 Mb heap

512 Mb heap

チューニングによるパフォーマンス改善結果

Page 56: HP-UX Java パフォーマンス・チューニングガイド

57/57

(付録 B) HP-UX HotSpot JVM の Permanent 領域について

HotSpotには Permanent領域というヒープ領域があります。ここにはクラスの内部表現が展開されます。具体的には、クラス、メソッド、フィールドなどのメ

タデータです。 一般に多くのアプリケーションにとって、Permanent領域のデフォルトのサイズは十分な大きさですが、アプリケーションによっては非常に多くのクラスをロ

ードするものもあり、Permanent領域が足りなくなることがあります。このような場合には、Permanent領域のサイズを変更する必要があります。Permanent領域を多く消費する典型的な例は、JSPや servletなどです。

オプションによる

サイズの指定

Java1.3.1.x ではこの Permanent 領域のサイズは、-XX:PermSize と

-XX:MaxPermSize の2つで指定することができます。単位は、バイトです。 $ java –XX:PermSize=32m –XX:MaxPermSize=64m TestProg

オプション名 意味 デフォルト値

-XX:PermSize Permanent Space の初期値 1MB

-XX:MaxPermSize Permanent Space の最大値 64MB

表 1. Permanent Space のサイズを指定するオプション

Java1.2.2では、デフォルトで初期値が 1MB、最大値が 32MBです。-XX:permSizeや-XX:MaxPermSizeでサイズを指定する場合、単位はキロバイトです。

Java1.3.1では、これらの値は図 1のように、-XheapInitialSizesを付けて javaを実行すると確認できます。

$ java -XheapInitialSizes -version NewRatio: 3 SurvivorRatio: 8 MaxTenuringThreshold: 32 Survivor size: 196608 Eden size: 1966080 New Size reserved: 22347776 initial: 2359296 Old Size reserved: 44761088 initial: 1441792 Perm Size reserved: 67108864 initial: 1048576 java version "1.3.1.01-release" Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1.01-release-010816-12:37) Java HotSpot(TM) Server VM (build 1.3.1 1.3.1.01-release-010816-13:34-PA_RISC2.0 PA2.0, mixed mode)

Page 57: HP-UX Java パフォーマンス・チューニングガイド

58/58

図 1. ヒープの初期値

Permanent領域がいっぱいになると full GCが実行されます。GC後も Permanent領域の割り当て要求を満たすことができない場合、VMは Permanent領域を拡張します。Permanent領域は、初期値の大きさから最大値の大きさまで必要に応じて拡張されます。クラスが GCの対象になるのは、そのクラスをロードしたクラスローダへの参照が無くなった場合(そのクラスローダが unreachableになった場合)のみです。

Permanent Space

不足によるエラー

JSPや servletを多用するアプリケーション(アプリケーションサーバなど)は、デフォルトの Permanent領域サイズでは足りなくなることもあります。Permanent領域を使い果たしてしまった場合は、次のエラーが表示されます。このような場合には、-XX:MaxPermSizeで十分な大きさを指定してください。

$ java ManyClassLoadingTest Permanent generation is full... increase MaxPermSize (current capacity is set to: 67108864 bytes ) Permanent generation is full... increase MaxPermSize (current capacity is set to: 67108864 bytes ) Exception in thread "main" Permanent generation is full... increase MaxPermSize (current capacity is set to: 67108864 bytes ) Permanent generation is full... increase MaxPermSize (current capacity is set to: 67108864 bytes )

図 2. Permanent 領域不足時のエラーメッセージ

クラスローダによる

Permanent領域への

クラスのロード

クラスファイルのロードは ClassLoader を使って行われます。ClassLoader には、デフォルトクラスローダとユーザ定義のクラスローダの2種類があります。 デフォルトクラスローダは、はじめから JVMに備わっているもので、システムクラスはこのクラスローダでロードされます。アプリケーションのクラスも普

通はこのクラスローダでロードされます。

Page 58: HP-UX Java パフォーマンス・チューニングガイド

59/59

ユーザ定義クラスローダは、ClassLoaderクラスを継承して作成するものです。これによりユーザは作成したクラスローダを使ってクラスをロードすることが

出来ます。

Permanent領域と

Garbage Collection

Permanent領域のクラスは以下の2つを満たす場合のみ、Full GCの対象になります。

ユーザ定義クラスローダによりロードされたクラスである ユーザ定義クラスローダへの参照が無くなっている(root setか

ら unreachableになっている) つまり、デフォルトクラスローダでロードしたクラスや、ロードに使用したク

ラスローダへの参照が残っているクラスの場合、Full GCが実行されてもその対象とはなりません。 また、Full GCが実行されるのは、以下の場合です。

ヒープのOld領域が拡張されるときと最大値に達したとき 「Old領域の空き領域 < New領域内の全オブジェクトサイズ」

の場合(GC reason 5) Permanent領域が拡張されるときと最大値に達したとき System.gc()が実行されたとき

-Xnoclassgcオプションを付ければ、Full GC実行時に Permanent領域を GCの対象外にすることができます。 以下は、GC直前での Permanent領域のサイズをプロットしたグラフで、-Xverbosegcの出力をファイルに書出し、そのファイルを HPjtuneで見たものです。この例では、Permanent領域が拡張されるたびに Full GCが行われています。また、Full GCが行われても Permanent領域サイズが減少せずに 64MBすべてを使い果たしています。64MBのヒープを使い果たした後、図 2のエラーを表示してJVMは終了します。Permanent領域を拡張するか、クラスローダの利用方法を見直せば(ユーザ定義クラスローダを使い、かつ不要なクラスオブジェクトに対

応するユーザ定義クラスローダへの参照をなくす)、Permanent領域が fullになるのを防ぐことができます。

Page 59: HP-UX Java パフォーマンス・チューニングガイド

60/60

図 3. Permanent領域の使用量

クラスのロード・アンロードは、-verbose:classオプションを付ければ見ることができます。

Permanent領域の

チューニング

動的にロードするクラスの数が有限であれば Permanent領域のサイズもある値で飽和します。-Xverbosegcを指定して Javaを起動し、すべてのアプリケーションのクラスをロードして、その時点の Permanent領域のサイズを調べ、その値をもとに-XX:MaxPermSizeで指定するサイズを決めてください。 クラスのロードにデフォルトクラスローダを使っているのかユーザ定義のクラ

スローダを使っているのか、またユーザ定義のクラスローダを使っていてもそ

のクラスローダへの参照をどのように管理しているかによって Permanent領域がFull GCの対象になるかどうかに違いが出てきます。これは、アプリケーションサーバがクラスローダをどのように定義・利用しているかに依存します。

Page 60: HP-UX Java パフォーマンス・チューニングガイド

61/61

(付録 C) HP-UX 上での Cヒープと Java ヒープ

HP-UXで Javaを起動した場合、ヒープには Cヒープと Javaヒープの2種類があります。 Cヒープは、C/C++のプログラムで使用する領域で、JVMが直接この領域を使用します。ここには Javaオブジェクトは置かれません。Cヒープはデータセグメント内に存在し、maxdsizで指定したサイズまで拡張されます。 Javaヒープとは、Javaプログラムのオブジェクトが置かれる領域で、-Xmnや-Xmxなどで指定できる New領域とOld領域、及び Permanent領域です。これらの領域は GCの対象になります。Javaヒープは Java VMが HP-UXの mmap() システムコールを使って割り当てるメモリ領域で、Cヒープとは別の領域です。 Cヒープと Javaヒープのどちらが不足しても、JavaのOutOfMemory Exceptionが起きます。Cヒープがメモリ不足になるのと、Javaヒープがメモリ不足になるのとは意味が違い、glanceでチェックする領域も異なります。 HP-UX 11i上の Java1.3.1では JVMは 32bitモードで動作するので、プロセスのメモリ空間全体は 4Gbyteです。 Java 1.4以降では 64bitモードをサポートがされるのでメモリ空間全体は理論上 16Tbyteと格段に増加し、ヒープに使用できるメモリサイズの制限も飛躍的に緩和されます。 ただし、どちらの場合についても物理メモリとスワップスペースのサイズの制

限を受けます

GlancePlusではモリ

消費量がどのように

見えるか

GlancePlusで javaプロセス(Java 1.3.1)のメモリ領域を見ると、下図のように見えます。 Cヒープはデータセグメント、Javaヒープは mmap() 領域にあることがわかります。Java 1.2.2では、Javaヒープは1つの領域として表示されます。 Java アプリケーションのメモリ消費量(メモリ上のオブジェクトのサイズ)は、正確には-Xverbosegcの出力を hpjtuneを使って見ることができます。

Page 61: HP-UX Java パフォーマンス・チューニングガイド

62/62

Page 62: HP-UX Java パフォーマンス・チューニングガイド

63/63

(付録 D)Java ヒープサイズの制限について(PA-RISC 版 SDK)

hp-uxの 32ビット プロセスにおける Large heapサポート

hp-ux SDK 1.3の javaプロセス及び SDK1.4の 32ビットモード javaプロセスのような hp-uxの 32ビットプロセスでは 4GBのメモリ空間のアドレスを指定することができますが、hp-uxではメモリ管理の効率を上げるために、この 4GBのメモリ空間を 4つに分割して各 1GBごとに sharedか privateかを指定します。このしくみのもと、hp-uxのメモリ空間は各リリースにおいて次のように変わってきています。 o 以前の hp-uxは最初の 1GBが TEXT(shared)、次の 1GBが DATA(private)、そのあとの 2GBが shared library等(shared)と固定になっていました。つまりデータ領域に 1GBしかとれませんでした。 o 10.xからは EXEC_MAGICを指定してコンパイルすることにより、最初のTEXT領域の 1GBを privateにできるようになり、最初の 2GBにデータを置けるようになりました。 EXEC_MAGIC・NORMAL_MAGIC・SHMEM_MAGICはいわゆるマジック ナンバーで、コンパイル時にコンパイルオプションで指定することにより異なる特性の実行形式を作成できます。 o 11.0にパッチをいくつかあてることにより、Large heapがサポートされるようになりました(11.0 ACE9911以降ではこれらのパッチは不要です)。 PHKL_21039, PHKL_20222, PHKL_20223, PHKL_20224, PHKL_20225, PHKL_20226, PHKL_20227, PHKL_20228 PHKL_20174 これにより、3番目の 1GBも privateにできるようになり、全体で最初の 3GBにデータを置けるようになりました。これは、chatrコマンドを使って「chatr +q3p 実行ファイル」 を実行することにより実現されます。設定された情報は「chatr 実行ファイル」でみることができます。 o 11iではさらに4番目の 1GBも「chatr +q4p 実行ファイル」を 実行することで privateにできるようになり、メモリ空間のほとんどに privateデータをおけるようになりました。 実際には 0xc0000000を syscall gateとして空けておかなくては いけないなどいくつか制限があります。 当然ながら、これには物理メモリ+スワップサイズの大きさが十分とられてい

ることが前提です。

Large heapと Java hp-uxの JVMには、java_q3p(3GBが private)と java_q4p(4GBすべてがprivate)という2つの実行ファイルがあり、指定されたヒープのサイズにより起動する JVMを切り替えています。

Page 63: HP-UX Java パフォーマンス・チューニングガイド

64/64

hp-ux 11.0では、1.3.1.05までの Javaは Large heapが使えず、最初の 2GBの領域にしか Java heapなどのデータを置けませんでしたが、1.3.1.06からは3GBまでの領域に Java heapを置くことができるようになりました。hp-ux 11iでは、このような制限は無く、ほぼ全領域にあたる 3.8GBのにヒープをとることができます。 (但し、3GB以上の Javaヒープを利用する場合は、New領域、または、Old領域のどちらかがおよそ 850MB以下でなくてはいけません。これは HP-UXの仮想アドレス空間管理のセグメンテーションによる制限です。また、Javaヒープが割り当てられるプライベート領域は、スレッドスタックや Cヒープも割り当てられるのでその兼ね合いによる制限も受けます。) SDK 1.4の 64ビットモードを使用すれば、上記のようなデータサイズの制限は無くなり、実質、物理メモリ+スワップサイズの制限が効いてくることになり

ます。 リリース毎の Javaヒープサイズの詳しい情報についてはリリースノートを参照してください。

Page 64: HP-UX Java パフォーマンス・チューニングガイド

65/65

(付録 E) Java スタックトレースによるリソース競合検出

Hotspot 1.3での Javaスタックトレース解析によるリソース競合の検出について説明します。 “2-6 リソースを大量に消費するメソッド・コール” に記述されているようにリソース競合の検出は、通常 HPjmeter の利用によって効率よく行えます。しかし、Javaサーバ・プログラムがクライアント・プログラムに対して反応しなくなった時などは、Hpjmtereでプロファイリングデータをプログラム実行後に解析するよりも、正にその時点の状態の Javaスタックトレースを解析してリソース競合状況を把握する方法がリソース競合検出に有効な場合があります。 ここでは、Javaスタックトレースのリソース競合状態時の特徴について述べます。 Javaスタックトレースは javaプロセスに SIGQUITシグナルを送る(%kill –SIGQUIT <java process id>)か、もしくは、javaプロセスがフォアグランド状態であれば<Ctrl>+\ (バックスラッシュ) を実行することで、java プロセスを終了させることなく、その時点での各スレッドのスタックトレースを標準出力に出

力することができます。 各スレッドスタックのヘッダは以下のようになっています。 “Thread2" prio=9 tid=0x1e8560 nid=10 lwp_id=13109 runnable [0x6fc16000..0x6fc16438]

前から順に、以下の意味です。 Javaプログラムが付けたスレッド名 prio :Javaプログラム内でこのスレッドに対し割り当てられた優先順位 tid :Hotspot C++オブジェクトへのポインタ nid :pthreadの id lwp_id :glance内で表される tidに対応 状態 [x..y] :このスレッドのスタック領域

ロックによる スレッドの同期

Javaでは共有リソースに複数のスレッドが同時にアクセスできないように オブジェクトに対してロックを設定することができます。(全てのオブジェクトには、対応するロックが存在します。) 1つスレッドだけが排他的に行うべきコードの範囲(クリティカルセクション)は以下のように実行されます。

まず、スレッドはクリティカルセクションの実行を行うまえに、その処理が実装されているオブジェクトに対してロックを設定しようとします。

a. そのオブジェクトにロックが設定されていない場合

Page 65: HP-UX Java パフォーマンス・チューニングガイド

66/66

オブジェクトにロックを設定し(スタックトレース 内にロックに関する情報として "locked"が含まれる)、クリティカルセクションの実行を行い、実行終了後にそのロック解除します。

b.そのオブジェクトに対し既に他のスレッドがロックを設定していた場合

ロックを設定したスレッドがその オブジェクトのロックを解除し、ロックを設定しようとしているスレッドがそのオブジェクトのロッ

クを 設定できるまで待ちます。 (そのときスレッドの状態は "waiting for monitor entry"となっており、スタックトレース 内で ロックに関する情報として"waiting to lock"が含まれる。) ロックを設定出来たら、クリティカルセクションを実行し、実行後

にそのロックを解除します。 また、スレッドがロックを設定した後、リティカルセクションを実行中に、なんらかの 原因で実行条件が成立していないために実行を続けることができないような場合は、waitコールを行い実効条件が成立するまで(notifyで通知されるまで)待機することもあります。(そのときのスレッドの状態は "waiting on monitor"となっており、スタックトレース 内にはロックに関する情報として"waiting for lock"が含まれます。)

スレッド状態

以下は、上記の”ロックによるスレッドの同期”の仕組みを踏まえた上で、リソース競合によるパフォーマンスの劣化が起こっているかどうかを分析するときに

注目すべきスレッド状態(”runnable”状態、”waiting on monitor”状態, ”waiting for monitor entry”状態)の説明です

①“runnable”状態 通常、スレッドが処理を行っているときは状態は”runnable”です。これはその時点で、javaコードを実行しているかネイティブメソッド内であることを示しています。 以下は I/O待ちのソケットコールで I/O待ち状態のスレッドですが、Javaバーチャル・マシンからみると実行(runnable)状態です。

"Thread-0" prio=9 tid=0x1bef58 nid=8 lwp_id=13105 runnable

[0x6fc58000..0x6fc58438]

at java.net.SocketInputStream.socketRead(Native Method)

at java.net.SocketInputStream.read(SocketInputStream.java:90)

at java.net.SocketInputStream.read(SocketInputStream.java:106)

at SocketClose$SocketListener.run(SocketClose.java:18)

at java.lang.Thread.run(Thread.java:479)

②”waiting on monitor”状態 waiting on monitor"状態のスタックトレースの例を以下に示します。スレッドダンプの出力内に以下のロックに関する情報が含まれるのが特徴です。

waiting on : locked

Page 66: HP-UX Java パフォーマンス・チューニングガイド

67/67

"waiter 2" prio=9 tid=0xfe460 nid=10 lwp_id=16212 waiting on monitor

[0x6fce7000..0x6fce7478]

at java.lang.Object.wait(Native Method)

- waiting on <0x68c15260> (a ObjectWaiterApp)

at java.lang.Object.wait(Object.java:424)

at WaiterThread.ComputeSome(ObjectWaiterApp.java:107)

- locked <0x68c15260> (a ObjectWaiterApp)

at WaiterThread.run(ObjectWaiterApp.java:158)

"waiter 1" prio=9 tid=0xfdfc8 nid=9 lwp_id=16211 waiting on monitor

[0x6fd08000..0x6fd08478]

at java.lang.Object.wait(Native Method)

- waiting on <0x68c15260> (a ObjectWaiterApp)

at java.lang.Object.wait(Object.java:424)

at WaiterThread.ComputeSome(ObjectWaiterApp.java:107)

- locked <0x68c15260> (a ObjectWaiterApp)

at WaiterThread.run(ObjectWaiterApp.java:158)

これらのスレッド先に述べたように、オブジェクトに関するロックを獲得後、

Object.wait()コールによって、一旦ロックを開放し、通知(notification)を待っている状態です。 一つのモニタに対して多くのスレッドが waitingの状態であることは必ずしも悪いことではありません。それはアプリケーションの設計に依存します。つまり、

設計によってはスレッド間の同期を synchronized methodの代わりに wait()を使う場合があるからです。しかし、同期アルゴリズムの実装において誤用をしな

いように注意をする必要があります。

③”waiting for monitor entry”状態 waiting for monitor entry"状態のスレッドが多く存在する場合は、望ましくない状態であるといえます。スレッドダンプの出力内に以下のロックに関する情報が含まれます。

このような状態のスレッドが多数ある場合は、設計に問題があったり、割り当

てたリソースがスレッド数に対して少なすぎることを示しています。

"msg 10-985216529353" prio=9 tid=0x1bad30 nid=61 lwp_id=12190

waiting for monitor entry [0x67642000..0x67642478]

at MsgThread.rest(ObjectWaiterApp.java:203)

- waiting to lock <0x6bcc0978> (a java.lang.Class)

at MsgThread.run(ObjectWaiterApp.java:222)

"msg 0-985216529350" prio=9 tid=0x2a7980 nid=60 lwp_id=12189

waiting for monitor entry [0x67708000..0x67708478]

at MsgThread.rest(ObjectWaiterApp.java:203)

- waiting to lock <0x6bcc0978> (a java.lang.Class)

at MsgThread.run(ObjectWaiterApp.java:222)

"msg 10-985216529256" prio=9 tid=0x1b9b30 nid=58 lwp_id=12187

waiting for monitor entry [0x67684000..0x67684478]

at MsgThread.rest(ObjectWaiterApp.java:203)

- waiting to lock <0x6bcc0978> (a java.lang.Class)

at MsgThread.run(ObjectWaiterApp.java:222)

Page 67: HP-UX Java パフォーマンス・チューニングガイド

68/68

デッドロック

複数のスレッドがお互いに依存したリソース競合を起こした結果、処理が進ま

なくなった状態をデッドロックといいます。

以下の例は、2つのスレッドがお互いに相手が保持しているロックに対して”waiting for monitor entry”状態であるためにデッドロックを起こしている例です。デッドロックはアプリケーションに問題があることを示しています。原因

を追求して問題を解消することが必要です。

"msg 2" prio=9 tid=0x155080 nid=31 lwp_id=16245 waiting for monitor

entry [0x67852000..0x67852478]

at MsgThread.rest(ObjectWaiterApp.java:203)

- waiting to lock <0x6bcc0af0> (a java.lang.Class)

at MsgThread.doMoreWork(ObjectWaiterApp.java:196)

- locked <0x68c16450> (a java.lang.Object)

at MsgThread.run(ObjectWaiterApp.java:224)

"msg 4" prio=9 tid=0x1bd3c8 nid=29 lwp_id=16243 waiting for monitor

entry [0x67894000..0x67894478]

at MsgThread.doMoreWork(ObjectWaiterApp.java:195)

- waiting to lock <0x68c16450> (a java.lang.Object)

at MsgThread.rest(ObjectWaiterApp.java:207)

- locked <0x6bcc0af0> (a java.lang.Class)

at MsgThread.run(ObjectWaiterApp.java:222)

Page 68: HP-UX Java パフォーマンス・チューニングガイド

69/69

(付録 F) OutOfMemoryErrorについて

Javaアプリケーションを実行中に"OutOfMemoryError"が発生してプログラムが異常終了してしまう場合があります。その原因は、エラーメッセージが示すよ

うにメモリ領域(システムメモリ、Javaヒープ)の不足が主なものですが、それ以外にメモリ領域不足が直接の原因ではない場合もあり、問題の特定の際に

混乱を引き起こす可能性もあります。ここでは、"OutOfMemoryError"が発生する以下の主な原因それぞれについて、エラーメッセージ例、確認方法、対処方

法について説明します。 - Javaヒープ(Old領域)不足 - 仮想メモリ不足 - カーネルパラメータ値が小さすぎる

ただし、エラーメッセージ 例についてはシステムの状況によっては異なるメッセージが発せられる場合もあります。

J a v a ヒープ

(Old領域)不足

Javaアプリケーションとして最も注意すべきのがこの Javaヒープ領域サイズです。システムのメインメモリに余裕があるにもかかわらず、このエラーがで

た場合は、Javaヒープ不足の可能性が高いといえます。 Javaヒープ不足のケースも大きくわけて2種類あります。

1. アプリケーションの正常動作のために必要な領域が確保されていない 2. Java メモリリークやプログラムの設計上の問題により、不要なオブジェクトが残ったままになってしまい結果的に Javaヒープが不足してしまう

基本的にはアプリケーションの実行に必要な Javaヒープの量の見積りを行ったり、実際にアプリケーションを実行したときのメモリ利用量を測定を行うな

どして、適切な Javaヒープの必要量の把握、及び、メモリリークの発見、修正を行い、適切な量の Javaヒープの確保した上で、不足する事態におちいらないようにすることが必要です。

エラーメッセージ例: Exception in thread "main" java.lang.OutOfMemoryError

<<no stack trace available>> 確認方法:

大きなヒープサイズを指定して(-XX:+AggresiveHeapオプションの利用が可能な場合は指定するなどして)、エラーの発生がなくなれば、Javaヒープ不足の可能性が高いといえます。

HPjtuneを使い GC後のOld領域内のオブジェクトの量の変化をチェックし、 使用量がOld領域を越えている場合は Javaヒープ不足の可能性が高いといえます。(見積もり以上にOld領域内のオブジェクトが増加している場合は Javaメモリ・リテンションの疑いがあります。)

Page 69: HP-UX Java パフォーマンス・チューニングガイド

70/70

対処方法:

Javaヒープ領域サイズ(-Xmxオプションなどで)を適切な量まで増やします

プログラムが生成するオブジェクト量を減らせないか検討します。 メモリ・リテンションがあれば修正します(2-7 メモリ・リークの章参照)

仮想メモリ不足

仮想メモリ不足とはシステムが利用できるメモリが不足してしまった状態です。

Javaヒープの利用量が増加し、システム全体で必要なメモリ量が、仮想メモリ量(物理メモリ量+スワップスペース量)をこえた場合にこのエラーが発生します。 エラーメッセージ例:

"Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread"

同時にカーネルが以下のようなエラーメッセージを発する場合もあります。 - exec(2): insufficient swap or memory available.` - cannot fork: no swap space

確認方法:

実行時に swapinfo(1M)や、システム解析ツール GlancePlusでスワップの利用量(GlancePlusメインメニュー“Reports”→”Swap Space”)を観察し、swapが不足していないかを観察します。

対処方法:

メモリリークがあれば修正します(2-7 メモリ・リークの章参照) プログラムが必要なメモリ量を減らせないか検討します 物理メモリ、swap領域を増設します 擬似スワップを利用していない場合は、利用を検討します(カーネルパラメータ SWAPMEM_ON)

カーネル

パラメータ値

が小さすぎる

カーネルパラメータの設定による制限を受ける場合があります。この場合は カーネルパラメータの値を増加させることで制限を緩和することができます。 エラーメッセージ例: max_thread_procの値が必要量より小さい場合

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

maxdsizeの値が必要量より小さい場合

Page 70: HP-UX Java パフォーマンス・チューニングガイド

71/71

Exception in thread "main" java.lang.OutOfMemoryError: requested XXXX bytes Possible causes:

- not enough swap space left, or - - kernel parameter MAXDSIZ is

very small.

確認方法: sam (1M)の[Kernel Configuration] →[Configurable Parameters]で各パラメータの設定値を確認します

GlancePlusをつかいそれぞれ以下の項目をチェック。 max_thread_procについて

javaプロセスのスレッド数が max_thread_proc数に到達しそうかどうかをチェック(GlancePlusメインメニュー”Reaports” →“process list”から、javaプロセスを選択し、”Reports”→ “Process Thread List”を選択)

maxdsizeについて javaプロセスの VSSのサイズが maxdsizeに到達しそうかどうかをチェック(GlancePlausメインメニュー”Reaports” →“process list”から、javaプロセスを選択し、”Reports”→“Process Memory Regions”を選択)

対処方法:

sam(1M)の[kernel configuration]や kmtune(1m)等を使い、現在のシステムの設定値と使用量を観察し、カーネルパラメータを適切な値に増やしま

す。 (HP-UX 11iでは max_thread_procはダイナミック調整可能なパラメータになったためリブートが必要ありませんが、HP-UX 11.0ではリブートが必要です。)

Page 71: HP-UX Java パフォーマンス・チューニングガイド

72/72

(付録 G) SDK 1.4 での新しいガベージコレクション(GC)

SDK 1.4.1から 2種類のパラレル GC、および、1種類のコンカレント GC 合計 3種類の新しいガベージコレクション(GC)が利用できます。(サポート状況の詳細については各 SDKリリースノートを参照してください) 以下に 3種類の GCの JVMオプションを示します。

2種類のパレレル GCのうちの 1つ(-XX:+UseParNewGC)は、コンカレント GC (-XX:+UseConcMarkSweepGC)と組み合わせることができ、リアルタイムに近い処理や GC停止時間が大きな問題である処理を行うアプリケーションに対して効果が期待できます。もう1つのパラレル GC(-XX:+UseParallelGC)は主にエンタープライズのような大規模なシステムもしくはスループット重視のアプリケ

ーション(J2EE等)に効果的だといえます。尚、パラレル GCの対象は New領域で、コンカレント GCの対象はOld領域です。以上の説明をまとめたのが以下の表です。

GC種類 GC対象領域 JVMオプション 主な効果

-XX:+UseParNewGC アプリケーション

停止時間減少

パラレル GC New領域 -XX:+UseParallelGC

スループット向上 (特にギカバイトクラの

ヒープサイズ、多 CPU

時に有効)

コンカレント GC OLD領域 -XX:+UseConcMarkSweepGC アプリケーション

停止時間減少 (注) JDK1.4.1.01では-XX:+UseParallelGCは XX:+UseConcMarkSweepGCと同時には指定できません。 以下はパラレル GCとコンカレント GCについての簡単な説明です。

パラレル GC

パレレル GCはこれまで New領域の GCを 1つのスレッドが行っていたのと違い、CPUと同数のスレッドが並列的に GCを行います。

アプリケーション スレッド

GCスレッド アプリケーション停止時間

Page 72: HP-UX Java パフォーマンス・チューニングガイド

73/73

コンカレント GC

コンカレント GCはガベージコレクションの処理の一部をアプリケーションスレッドと同時行います。その結果、アプリケーションスレッド全てが停止する

時間を減少させることができます。

コンカレント GC関連のオプションには以下のものがあります。 -XX:ParallelGCThreads=<n>

パラレル GCを行うスレッド数を指定します。 (デフォルト値: CPU数)

-XX:CSMInitiatingOccupancyFraction=<n> GCを開始する場合の Old領域のオブジェクトの割当の割合を指定します。

(デフォルト値:68) コンカレント GCが完了しない場合が多いときは、この値を小さくします。 (次章 の”HPjmeterサポート”の項目を参照)

アプリケーション停止時間

アプリケーション スレッド

GCスレッド

Page 73: HP-UX Java パフォーマンス・チューニングガイド

74/74

付録 H) HP-UX 11i Version 2(11.23) 、Java2 1.4 パフォーマンス関連情報

Java2プラット

フォーム

以下の表は、HP-UXのバージョンとプロセッサアーキテク(チャ、Java2バージョンの対応を示したものです。 HP-UXバージョン プロセッサ

アーキテクチャ サポート Java2 バージョン

HP-UX 11i Version 1 (11.11)

PA-RISC 1.2,1.3,1.4

HP-UX 11i Version 1.6 (11.22)

Intel Itanium 1.3 ,1.4

HP-UX 11i Version 2 (11.23)

Intel Itanium 1.3,1.4

本章は、主に HP-UX 11i Version 2(11.23)、Java2 1.4環境についての情報です。

パフォーマンス

チューニングツール

本書で紹介した以下のパフォーマンスチューニングツール全てが Itaniumアーキテクチャ(HP-UX 11.22, 11.23)でもサポートされています。

- HPjconfig (カーネルコンフィグレーションツール) - HPjtune (ガベージコレクション解析ツール) - HPjmeter (Javaアプリケーションパフォーマンス解析ツール) - Glance Plus

JavaOutOfBox(カーネルパラメ-ータ設定パッチ)については、Itaniumアーキテクチャ(HP-UX 11.23)ではデフォルト値として Javaサーバアプリケーションに適切な値が既に設定されているため、適用する必要はありません。 HPjconfigは HP-UX 11.11ではカーネルパラメータ値設定のために SAM(1M)用のファイルを出力していましたが、HP-UX 11.23では設定用のシェルスクリプトを生成(HPjcofnig [SUPER USER]パネル)します。生成されたシェルスクリプトをsuper userとなって実行すると、設定したカーネルパラメータの変更が行われます。 HPjtune対応については以下の “HPjtuneサポート”の項目を参照してください。

カーネルコンフィグ

レーション

HP-UX 11.22,1123ではパフォーマンスチューニングの際に必要な、カーネルコンフィグレーションについて機能強化が行われました。

ダイナミック調整可能カーネル パラメータの追加 カーネル コンフィグレーション ツール(kcweb)

Page 74: HP-UX Java パフォーマンス・チューニングガイド

75/75

ダイナミック調整可能カーネルパラメータ ダイナミック調整可能カーネルパラメータはシステムをリブートすることなく

その値を変更でき、ダウンタイムの削減に役立ちます。HP-UXではバージョンを重ねるごとにダイナミック調整可能なカーネルパラメータが追加されています。 HP-UX 11.11(PA-RISC) HP-UX 11.22(Itanium) HP-UX 11.23 (Itanium) shmmax maxuproc shmseg maxfiles_lim(*) maxtsiz msgmnb msgmax maxtsiz_64bit core_addshemem_read core_addreshmem_write scsi_max_qdepth semmsl

このリリースでの追加 maxdsiz(*) maxdsiz_64bit maxssiz(*) maxssiz_64bit nkthread(*) nproc(*) max_thread_proc(*) ksi_allco_max shmmni secure_sid_scrpts max_acct_file_size

このリリースでの追加 dbc_max_pct dbc_min_pct nfile(*) nflocks

(*)J2EEシステムのチューニング時によく変更されるものです。 カーネルパラメータの変更はコマンドを利用したり、次節のカーネルコンフィ

グレーションツール(kcweb)を利用することもできます。(詳細については「HP-UXシステム/ワークグループの管理」 (http://www.docs.hp.com/ja/5187-2219/index.html )を参照して下さい。) カーネル コンフィグレーション ツール(kcweb)

kcwebは HP-UX 11.22, 11.23に付属の非常に強力なカーネルコンフィグレーションツールです。カーネル全般のコンフィグレーションを webベースの GUIを使って直感的な操作で効率良く行うことができます。カーネルパラメータを変

更する必要性が生じた場合は、この kcwebを利用出来ます。 (HP-UX 11.11 に関しては以下のサイトからベータ版を入手することができます。http://software.hp.com/products/KCWEB/index.html) kcwebを利用するとパラメータの関連情報(パラメータの意味、ダイナミック調整の可否、現在値、デフォルト値、他のパラメータとの関連、値の制限 等)も素早く知ることができます。 また、カーネルパラメータに関するリソースが実際にどれくらい消費されてい

るかをモニタする機能もあり、実際に現在のカーネルパラメータ値が適切であ

るかどうかを判断する材料を得ることもできます。

Page 75: HP-UX Java パフォーマンス・チューニングガイド

76/76

kcwebのカーネルパラメータ調整画面 (max_thread_procパラメータを選択)

Java2 1.4パフォーマンス 関連強化機能

Java2 1.4環境での主なパフォーマンスチューニングに関する強化機能については以下のものがあります。これらの機能は 11.11(PA-RISC), 11.22,11.23(Itanium)

のどちらのプラットフォーム上でもサポートされています。

64bitモード ガベージコレクション(GC)強化 GC プロファイリング強化 GC 監視オプション追加

以下、それぞれの項目についての説明です。 64bitモード Java2 1.4ではデフォルトの 32bitモードに加えて、64bitモードがサポートされています(-d64オプション)。これまでの 32bitモードの制限である最大ヒープサイズ PA-RISC:約 3800MB(HP-UX 11.11以降), Itanium:3500MB(HP-UX 11.23以降)より大きな量のヒープを利用できます。64bitモードでは 32bitのプロセス空間サイズの制限はなくなりますが、実際にはヒープサイズは物理メモリ、ディ

スクスペース、スワップスペースなどによって制限されます。一方、ガベージ

コレクションによる性能低下の観点からもあまり大きなヒープサイズは現実的

でない場合があります。

Page 76: HP-UX Java パフォーマンス・チューニングガイド

77/77

Javaヒープはデフォルトでは lazy swap属性を使ってメモリ領域を確保するので、実行時に実際にスワップが利用されるまで実際には領域を確保されません。

したがって、実行時に実際に利用できるスワップが足りない場合はプログラム

が実行途中で強制終了させられてしまう場合があります。メモリ、スワップの

サイズを決めるときは注意が必要です。 ガベージコレクション(GC)強化 Java2 1.4のガベージコレクション強化については”(付録 G) 新しいガベージコレクション(GC)”を参照してください。 GCプロファイリング強化 ガベージコレクション強化にともないプロファイリングデータも強化されまし

た。これらに変更については HPjtuneでも対応済みです。 プロファイルヘッダ Java2 1.4環境では、HPjtune用のプロファイルデータ(-Xverbosegcオプションでプロファイリング)には、ヘッダに新たに以下の情報が含まれます。これらの情報はチューニングの際のログとしても役立ちます。

- JVMバージョン、 - システム情報(メモリ量、スワップ量、CPU.数) - OSメモリ関連情報、 - ヒープパラメータ、 - ヒープ領域サイズ - GC種類

-Xverbosegcのプロファイルデータのヘッダ例(一部) <GCH: vgc=2.1 > <GCH: vm="Java HotSpot(TM) Server VM mixed mode" > <GCH: vmrelease="1.4.1 1.4.1.05-030910-05:38-IA64N IA64" > <GCH: starttime="Fri Jan 23 10:45:53 JST 2004" > <GCH: hostname=userhost01 > <GCH: os=B.11.23 > <GCH: machine=ia64 > <GCH: physmem=4187284 > <GCH: mode=n > <GCH: ncpu=2 > …. <GCH: argXms=0 > <GCH: argXmx=0 > <GCH: optNewSize=2304 > <GCH: optMaxNewSize=4194240 > <GCH: optOldSize=16384 > <GCH: optMaxHeapSize=65536 > <GCH: optNewRatio=2 > <GCH: optSurvivorRatio=8 > <GCH: optMaxTenuringThreshold=31 > ……. <GCH: optUseParNewGC=0 > <GCH: optUseParallelGC=0 >

Page 77: HP-UX Java パフォーマンス・チューニングガイド

78/78

パラレル GC(-XX:+UseParallelGC)対応 -Xverbosegcや-Xloggcで収集されたデータはパラレル GCの場合も有効でありHPjmeterで解析可能です。 コンカレント GC(-XX:+UseConcMarkSweepGC)サポート -Xverbosegc( Java2 1.4.1.05, 1.4.2以降)は、コンカレント GCもサポートしています。HPjtuneでも CMS(Concurrent Mark&Sweep)対応として以下のメトリクス等が追加されました。

Stop_the_World GC Time プログラム実行中にガベージコレクションによって、アプリケーショ

ンが停止した(stop_the_world)時間 Incomplete_CMS wasted time 完了しなかったコンカレント GC中の stop_the_world時間 (GCが完了していないので、この時間は浪費されたことになります。)

CMS concurrent runnig time コンカレント GCがアプリケーションと同時に実行された時間

詳細については以下のサイトを参照してください。 http://www.hp.com/products1/unix/java/java2/hpjtune/index.html GC監視オプション追加 HPjtuneで利用する –Xverbosegcオプションの他も目的に応じて GCに関する情報を出力することができます。(いずれも標準オプションではないので、将来

的に使えなくなる場合があるので注意が必要です) -Xloggc:filename

GC情報(一般的な情報をファイルへ出力) -XX:+PrintGCDetails

GC情報(詳細) -Xverbosegc

GC情報(詳細)を出力(HP-UX JVMのみ) -XX:+PrintTenuringDistribution

オブジェクトの年齢情報 -XX:+TraceGen0Time

New世代領域の累積 GC時間、GC回数、平均 GC時間 -XX:+TraceGen1Time

Old世代領域の累積 GC時間、GC回数、平均 GC時間 -XX:+PrintGCTimeStamps

GCのタイムスタンプ情報 -XX:+PrintHeapAtGC

GC前後の詳細なヒープ情報

Page 78: HP-UX Java パフォーマンス・チューニングガイド

79/79

(付録 H) HP-UX Java 5.0 パフォーマンス関連情報

GC エルゴノミクス

GC(ガーベッジ・コレクション)関連のチューニングによってフォーマンス改善が期待できます。しかし、チューニングを行うためには、GCポリシーの選択や適切なヒープサイズ設定などを適切に設定するための工数がある程度必要となりま

す。そこで、GC関連のチューニングの自動化を目指したものが GCエルゴノミクスです。 Java 5.0で実装された GCエルゴノミクスを以下に述べます。

デフォルト ヒープサイズ 2CPU以上のシステムでは、-Xms, -Xmxオプションを使って明示的にヒープサイズの初期値、最大値を指定しない場合、以下のように自動的に設定されま

す 初期ヒープサイズ 物理メモリ量 x 1/64 (ただし 最大 1GB)

最大ヒープサイズ 物理メモリ量 x 1/4 (ただし 最大 1GB)

GCポリシーの自動選択

GCポリシーを明示的にオプションで指定しない場合 システムの CPUが 2個以上、かつ、物理メモリ量が 2GB以上の環境では、パラレル GC(-XX:+UsePrallelGC)が選択されます。 (デフォルト GCを使うには -XX:+UseSerialGCを指定します)

GCポリシーにコンカレント・マーク&スイープ(-XX:+UseConcMarkSweepGC) を指定した場合

New世代にパラレル GC(-XX:+UseParNewGC)が使われます。

パラレル GCを行うスレッド数をオプションで明示的に指定できます。

(-XX:ParallelGCThreads=[スレッド数] ただし、スレッド数に 1を指定するとパラレル GCではなくデフォルトの Serial GCになります)

スレッド数を明示的にオプションで指定しない場合は以下にしたがってスレッド数が決定されます。

システムの CPUが 8個以下の場合

スレッド数=[CPU数]

システムの CPUが 9個以上の場合 スレッド数= 8 + [(CPU数-8)*5/8の整数部]

できるだけ Old領域での GCを行うために以下の調節を行います。

一時停止が短くなるような New領域のサイズ

Page 79: HP-UX Java パフォーマンス・チューニングガイド

80/80

少なくとも New領域の 3倍以上の Old領域サイズ 早めのオブジェクトの New領域から Old領域への移動

ただし、以下をオプションで明示的に指定すると以上は有効ではありません。

-Xmx, -Xms -XX:MaxTenuringThreshold -XX:SurvivorRatio

GCポリシーにパラレル GC(-XX:+UseParNewGC)を指定した場合

New世代にパラレル GC(-XX:+UseParNewGC)が使われます。

パラレル GCを行うスレッド数を明示的にオプションで指定できます。 (-XX:ParallelGCThreads=[スレッド数] ただし、1を指定するとパラレルではなくデフォルトの Serial GCになります。)。

スレッド数を明示的にオプションで指定しない場合は以下にしたがってスレッド数が決定されます。

システムの CPUが 8個以下の場合

スレッド数=[CPU数]

システムの CPUが 9個以上の場合 スレッド数= 8 + [(CPU数-8)*5/8の整数部]

パラレル GC(-XX:+UseParNewGC)をオプションで明示的に指定ない場合でも、スレッド数を指定(0,1以外)すればパラレル GCになります。

目標指向の GCチューニング

2種類のあたらしい GCチューニング方法が導入されました。 最大ポーズ時間目標(-XX:MaxGCPauseMills=<milliseconds>)

アプリケーション スループット目標(-XX:GCTimeRatio =<n >)

例) -XX:GCTimeRatio=20 スループット目標 = 1/20 = 5%

つまり、GC時間が全体の 5%以下にすることが目標

但し、目標については常に保障されるわけではありません。

GC エルゴノミクスを利用する場合でも-Xverbosegcの出力ヘッダや -XX:+InitialGCInfoなどで実際の設定内容を確認することをお勧めします。 また、GCエルゴノミクスの効果(特にヒープが 1G以下の場合)はある程度期待できますが、手動によるチューニングが必要なくなったわけではありません。

Page 80: HP-UX Java パフォーマンス・チューニングガイド

81/81

新しい Hotspotダイナミックコンパイラ

Itaniumプラットフォームの Java 5.0では新しい Hotspotダイナミックコンパイラが標準となり、コンパイル処理のフレームワークや最適化が改善されました。 (以前のダイナミックコンパイラを使うには オプションで–XX:+UseC2を指定します) これにより以下のようなメリットがあります。

品質の向上 先進的な IPFプラットフォーム固有の最適化 今後の継続的な品質、パフォーマンスの向上

スケーラビリティの強化

オブジェクトモニタと無駄な wakekupに関する改善

無駄な wakecupとは、スレッドの排他制御などのためのオブジェクトモニタ機構における、プログラム処理に関係ない wakecup(コンテキストスイッチ)のことです。これを削減することによってスケーラビリティが向上します。Java 5.0 以前ではあるスレッド(T1)が lockを開放すると、待ち状態であるスレッドすべて(T2, T2, T3, T4)に対し notifyされ、lock獲得のための競合が起きていました。

Java 5.0では競合しているスレッドはキューに置かれ、notificationは dequeue処理になったため無駄な wakecupが削減されました。(この機能は J2SDK1.4.2.08へバックポートされています)

xx

T1 T2 T3 T4

Inflate OS level

Lock for T2

OS level Lock for T3

OS level Lock for T4

notify

OS level Lock for T2

OS level Lock for T3

OS level Lock for T4

Queue

T1 release lock

Only T2 grabs the lock

Page 81: HP-UX Java パフォーマンス・チューニングガイド

82/82

リザーブ・ロッキングの利用 スレッドの同期の制御で利用されるロックの獲得、開放処理が、実際にスレッ

ド間の競合がない場合でも行われると、パフォーマンスへ悪影響を与える場合

があります。 Java 5.0のリザーブ・ロッキングでは競合がないことを前提とし、ロック 処理で使われるコストが高いアトミックな命令の代わりに loadや store命令を用います。そして実際に競合が起こった場合のみアトミックな命令を使います。そうする

ことによりロック競合処理によるパフォーマンスへの悪影響が少なくなってい

ます。

管理、監視フレームワーク(JMX)

Javaアプリケーションを管理、監視するための新しい標準フレーム ワークとして JMXが導入されました。 JMXは三つのコンポーネントから成り立っています。

MBean MBean サーバ 外部へのインタフェース(表示ツールの為のインタフェース)

MBeansはアプリケーション内部のコンポーネントやリソースのラッパーで、以下のようなシンプルなインタフェースを持っています。

管理対象リソースの属性のゲッター(Getters)とセッター(Setters) ゲッター: read (監視) セッター: write or modify (管理)

オブジェクトのメッソドに対し invokeオペレーション 例) JVM内部の GCの起動

管理対象リソースの状態変化へ応答するための通知(notification)

MBean Server

Adaptor Adaptor Client

Application

HTM

Application Resource

MBean

GarbageCollectorMXBean

Managed Resource: Java heap inside the JVM

“Read” generation size

Invoke

Notify console when limit is

Page 82: HP-UX Java パフォーマンス・チューニングガイド

83/83

Java 5.0では以下の JVM関連のリソースを監視、管理するMbeanのセットを標準でそなえています。

クラス ローディング システム ClassLoadingMXBean コンパイレーション システム CompilationMXBean メモリ システム MemoryMXBean スレッド システム ThreadMXBean ランタイム システム RuntimeMXBean オペレーティング システム OperatingSystemMXBean ガーベッジ コレクション システム GarbageColloctorMXBean JVM メモリ マネジャ システム MemoryMangerMXBean JVM メモリプール MemoryPoolMXBean

jconsole JDK5.0 では JMX ベースのリソース監視、管理ツール jconsole が標準のツールとして

利用できます。jconsol によって JVM 内部のリソースの利用状況などを把握することが

できるのでパフォーマンス チューニングに役立てることができます。 利用方法

ローカルで監視する場合

1. アプリケーションの起動 % java -Dcom.sun.management.jmxremote <…> <application>

2. jconsole の起動 % $JAVA_HOME/bin/jconsole <jvm process id>

リモートで監視する場合(SSL,認証無効)

1. アプリケーションの起動 % java -Dcom.sun.management.jmxremote.port=1090

-Dcom.sun.management.jmxremote.ssl=false

-Dcom.sun.management.jmxremote.authenticate=false

<…> <application>

2. jconsole の起動 % $JAVA_HOME/bin/jconsole

現れたウィンドウの Remote タブでホスト名、ポート名を設定

Page 83: HP-UX Java パフォーマンス・チューニングガイド

84/84

サマリ画面

アプリケーションに関する一般的な情報を表示

ヒープメモリ利用画面

Java ヒープ全体の利用量やヒープ内の各領域の利用量などを表示

Total amount of memory used by the garbage collector to this point

Serial GC

Overall

Eden

Survivor

Tenured

Perm Space

Code Cache

Page 84: HP-UX Java パフォーマンス・チューニングガイド

85/85

MBean オペレーション画面

引数に値を代入し、各オペレーションの実行が可能

jconsole ではさらにスレッド、クラスなどに関する情報についても取得でき

ます。

List of all the MBeans registered with the server

Getters and Setters

Invoke

Get thread info for threads to 11 for example

Page 85: HP-UX Java パフォーマンス・チューニングガイド

JHS04031-03

日本ヒューレット・パッカード株式会社 〒140-8641 東京都品川区東品川2-2-24 天王洲セントラルタワー

お問い合わせはカスタマー・インフォメーションセンターへ 

03-6416-6660 月~金 9:00~19:00 土 10:00~18:00(日、祝祭日、年末年始および5/1を除く)

HP-UX製品に関する情報は http://www.hp.com/jp/hpux 記載されている会社名および商品名は、各社の商標または登録商標です。 記載事項は2005年11月現在のものです。 本書に記載された内容は、予告なく変更されることがあります。 本書中の技術的あるいは校正上の誤り、省略に対して、 いかなる責任も負いかねますのでご了承ください。 © Copyright 2005 Hewlett-Packard Development Company,L.P.