この章の内容は次のとおりです。
プロファイラは、実行中のプログラムに関する統計を収集して、パフォーマンスの問題を診断し、コードの非効率な箇所を修正します。
次のプロファイリング機能を使用できます。
遠隔測定—CPU、メモリーの使用状況、スレッドおよびロードされたクラスの数をモニターします。「プロファイリング・テレメトリ」を参照してください。
メソッド—メソッド実行時間と呼出し数(呼出しツリーを含む)をプロファイルします。「プロファイリング・メソッド」を参照してください。
オブジェクト—割当てパスを含む、割り当てられたオブジェクトのサイズと数をプロファイルします。「プロファイリング・オブジェクト」を参照してください。
スレッド-—スレッドの時間および状態をプロファイルします。「スレッドのプロファイリング」を参照してください。
ロック—ロックのコンテンツ・データをプロファイルします。「ロックのプロファイリング」を参照してください。
テレメトリ・モードでは、次のメトリックを提供します。
CPUとGC—指定した時間に使用されるCPUおよびGCのパーセンテージを表示します
メモリー—ヒープ・サイズと指定した時間に使用されるヒープをMBで表示します
存続世代 —指定した時間の存続世代の数を表示します。また、GC間隔も表示します
スレッドとクラス—指定した時間にロードされるクラスおよびスレッドの数を表示します
プロファイリング・テレメトリ・セッションを起動するには、「プロファイラの起動について」を参照してください
図10-1はテレメトリ・セッションのスナップショットを表示します
図10-1 テレメトリ・セッション
デルタ値の表示-このアクションは絶対値から増分値に切り替えます。ビューを切り替える前に表示される値は記憶されますが、新規に選択された瞬間から新しいビューで変更が表示されます。このアイコンを再度クリックすると、結果が絶対値に戻されます。
スレッドの選択-この操作はライブ結果や保存されたスナップショットで使用できるスレッドを表示し、結果を表示する特定のスレッドの選択を可能にします。この機能は、デスクトップ・アプリケーションでEDTの遅さを追跡している場合、またはサーバー・アプリケーションでワーカー・スレッドを分析している場合に特に有用です。一部のスレッドが選択され、「すべてのスレッドの表示」オプションのために無効になっている場合、「選択したスレッドのマージ」オプションが有効になります。この機能は、結果を選択したスレッドから単一のツリーにマージします。
さらに、表示する列を選択できます。オプションは、「合計時間」、「合計時間(CPU)」、「選択済」および「ヒット」または「呼出し」(セッション構成に応じて)です。
プロファイリング・メソッド・セッションを起動するには、「プロファイラの起動について」を参照してください
図10-2はメソッド・セッションのスナップショットを表示します。
図10-2 メソッド・セッション
オブジェクト・モードでは、ライブ・インスタンスおよびバイト・アロケーションを含むプロジェクトに割り当てられているクラスのリストが提供されます。ページの右上隅にあるアイコンをクリックすると、プロファイルされるクラスを選択できるドロップダウン・メニューにアクセスします。「すべてのクラス」モードは、「仮想マシン」ヒープ上にあるすべてのクラスおよびオブジェクトを表示します。「プロジェクトのクラス」フィルタでは、プロジェクトで定義されているクラスのみを表示できます。
プロファイリング・オブジェクト・セッションを起動するには、「プロファイラの起動について」を参照してください
図10-3はオブジェクト・セッションのスナップショットを表示します
図10-3 オブジェクト・セッション
クラスを右クリックして、「プロファイル・クラス」を選択することで、特定のクラスをプロファイルするように選択できます。「ライブ・オブジェクトのみ追跡」および「割当ての深さを制限」チェック・ボックスが表示されます。
ライブ・オブジェクトのみ追跡— 選択されると、ライブ・オブジェクトのみを追跡します。選択されていない場合、アプリケーションによって割り当てられているすべてのオブジェクトを追跡します。
割当ての深さを制限— 指定した数にスタックの深さの割当てを制限します。
クラスを選択した後、変更を送信するプロファイリング・セッションが進行している間に、右側にある「適用」ボタンをクリックします。図10-4に示すように、この操作はビューをクリアして、選択したクラスのみを指定します
図10-4 オブジェクト・セッション - 選択済クラス・ビュー
スレッド・モードでは、アプリケーション・スレッド・アクティビティに関する詳細な情報を表示します。
プロファイリング・スレッド・セッションを起動するには、「プロファイラの起動について」を参照してください
図10-5はスレッド・セッションのスナップショットを表示します
図10-5 スレッド・セッション
さらに、「実行中のスレッド」ドロップダウン・リストにアクセスして、次の使用可能なオプションを選択することでモニターするスレッドをカスタマイズできます。「すべてのスレッド」、「実行中のスレッド」(デフォルト)、「終了スレッド」および「選択されたスレッド」。
ロック・モードでは、ロックされたスレッドおよびロックをモニターおよび保持しているスレッドの詳細を表示できます。
ロック・セッションのプロファイリングを起動するには、「プロファイラの起動について」を参照してください
図10-6はロック・セッションのスナップショットを表示します
図10-6 セッションのロック
セッション・ウィンドウでは、「スレッド」ドロップダウン・リストの「スレッド」または「モニター」を選択できます。ロックされたスレッドを表示するには、「スレッド」を選択します。ノードを開いてロックの所有者を表示します。他のスレッドをロックしているスレッドを表示するには、「モニター」を選択します。
プロファイラ・セッションが進行中、実際のプロファイラ・モードに関連する追加処理がプロファイラ・ウィンドウのツールバーで使用できます。次のアクションは常に使用できます。
スレッド・ダンプ—すべてのアクティブ・スレッドのテキストのダンプを作成し、プロファイルされたアプリケーションをモニターします。ダンプを取得した時点でどのメソッドが実行されているかをスレッドごとに表示します。この情報は、アプリケーションが今何を行っているかを表示する際に役立ちます。スレッド・ダンプには、ロック、ロックを保持しているスレッドおよびロックの取得を待っているスレッドに関する情報も表示されます。このデータはデッドロックをデバッグする際に必須です。「スレッド・ダンプ」を取得するには、プロファイリング・セッション中に「スレッド・ダンプ」アイコンをクリックします。スナップショットの取得に関する詳細は、「プロファイリング・データのスナップショットの取得およびアクセス」を参照してください
ヒープ・ダンプ—プロファイルされたプロセスの現在のヒープ・コンテンツのイメージを.hprof形式保存し、オプションでヒープ・ブラウザで開きます。詳細は、「ヒープ・ダンプ・データの取得」を参照してください
GC—プロファイルされたプロセスのJVMを要求し、ガベージ・コレクションを呼び出します。ガベージ・コレクションのJVM動作は、JVM仕様では定義されていません。どこかの時点でガベージ・コレクションを実行する必要がありますが、即座に実行するかまったく実行しないか何も保証しません。
さらに、メソッドまたはオブジェクトをプロファイリングする際には、次の処理が使用可能です。
スナップショット—メソッドまたはオブジェクトに関連する現在収集されているすべてのプロファイリング・データのスナップショットを作成します。スナップショットは別のウィンドウで開き、プロジェクトまたは外部ファイルに保存できます。詳細は、「プロファイリング・データのスナップショットの取得およびアクセス」を参照してください
収集結果をリセット—メソッドまたはオブジェクトに関連する現在収集されているすべてのプロファイリング・データをクリアします。
プロファイラ・ウィンドウのツールバーに表示されているその他の処理は実際のプロファイリング・モードに固有です。複数のプロファイリング・モードがプロファイリング・セッションでアクティブな場合、ツールバーは現在表示されているモードで使用できる処理を表示します。
プロファイリング・セッションの進行中にヒープ・ダンプを取得できます。ヒープ・ダンプを取得するときに、ヒープをプロジェクトまたはローカル・ファイル・システムに保存するよう要求されます。ヒープ・ダンプを保存した後で、随時ヒープ・ダンプをロードして、ヒープ上のオブジェクトを参照したり、個別オブジェクトの参照を特定したり、ヒープ・ダンプの比較によるスナップショット間の相違を確認したりすることができます。ヒープ・ダンプをロードしたり参照したりするために、プロファイリング・セッションが実行中である必要はありません。
ヒープ・ダンプを取得するには、アプリケーションがJDK 1.5.0_12またはそれ以降で実行している必要があります。
プロファイリング・ポイントを使用してヒープ・ダンプを取得する手順は次のとおりです。
プロファイリング・ポイントを配置するコードが含まれているソース・ファイルを開きます。
プロファイリング・ポイントを配置するコードの行を右クリックして、「プロファイリング・ポイントの追加」を選択します。
「プロファイリング・ポイント・タイプ」リストで、次のスナップショット・ポイントのいずれか1つを選択して「次へ」をクリックします。
スナップショットの取得
時間によるスナップショットの取得
トリガーによるスナップショットの取得
ウィザードの「プロパティのカスタマイズ」ページで、スナップショットのタイプとして「ヒープ・ダンプ」を選択し、追加設定を変更します。「ヒープ・ダンプ」オプションは、「設定」→「取得」から使用できます。
プロファイリング・ポイントを使用してヒープ・ダンプを取得する際、プロファイリング・ポイントを配置するソース・コード内のポイントを指定します。たとえば、スレッドが特定のメソッドに入ったときに、ヒープ・ダンプを取得する必要が生じることがあります。
OutOfMemoryエラー時にヒープ・ダンプを取得する手順は次のとおりです。
ヒープ・ダンプ・ビューア(ヒープ・ウォーカ)は、String値、ファイル・パス、URLアドレスなどの論理値を表示します。さらに、UI属性のビジュアル・スナップショットおよび要素(色、フォント、ボタンなど)を提供します。
ヒープ・ダンプに表示されているほとんどのクラスおよびインスタンスでは、テキストのプロパティおよび数字のプロパティがデータ構造の説明と検査およびメモリー・リーク、非効率なメモリー使用などのバグの検出に適しています。ただし、多くの種類のオブジェクトにおいて、インメモリー表現はオブジェクトが何であるかを即座に決定する場合には適していません。
ビジュアル表示機能は、UI要素の検査に最適です。ここでは、オブジェクト・プロパティの表示は、アプリケーションUIの正確な場所を識別できるほど精密ではありません。たとえば、UIコンテナのネストされた要素に対する位置、サイズおよび参照を単に読み込むだけでは、オブジェクトがアプリケーションによって作成された「ファイルを開く」ダイアログを表すことにユーザーが気づかない可能性があります。
指定したクラスのインスタンスを参照して、ヒープ・ダンプ要素のイメージ表現にアクセスします。図10-7は、ヒープがダンプされた時点で、アプリケーション・ウィンドウのビジュアル・プレビューを含むヒープ・ウォーカ・パネルを表示します。
図10-7 ヒープ・ウォーカ - イメージ・プレビュー
ヒープ・ウォーカ・イメージ・プレビューは次のユースケースで有用です。
選択したUI要素の識別。特定のUI要素(ボタン、ラベルなど)を検索する必要がある場合に、希望する種類のインスタンスを参照します
ヒープをダンプする時点でのアプリケーションの状態の判断。ユーザーによって報告されるバグは、必要なすべての情報が含まれていなかったり、重要な詳細情報が欠けていたりすることがよくあります。アプリケーションUIを表示することで、ユーザーはメモリー不足の例外がスローされた際にテキスト・ドキュメントがロード中であるなどが即座にわかります。
予定外にメモリーに残ってしまったUIスニペットの検索。UIのパーツはメモリーからリリースされていないことがあり、表、ツリーまたはエディタが大きなデータ・モデルを参照する場合に深刻な問題を引き起こす可能性があります。たとえば、「パネル」要素を参照することで、ユーザーはこれらのスニペットを簡単に検出でき、どれぐらいのメモリーが活用されていないかがわかり、UIのリリースを妨げている問題を識別できます。
重複の検出。イメージを参照することで、リソースを無駄にしている、メモリーに割り当てられている同じイメージの複数のインスタンスなどをユーザーは即座に表示できます。
オフラインUI分析。ヒープ・ウォーカはヒープ・ダンプからUI構造を再作成できます。この方法で、UI開発者は別の互換性のないシステムで実行されている可能性のある実際のアプリケーションにアクセスすることなく、UI作成ブロックを分析できます
イメージのプレビュー機能に対する次のような例外を確認することが重要です。
ツリー・データの表示はサポートされていません。
フォアグラウンド、バックグラウンドまたはフォント属性は特定の実装では表示されない可能性があります。
カスタム・コントロールは表示できません。
特定要素のUIテキストは「インスタンス」ビューではフルに表示されない可能性があります。
OQLはSQLに似た問合せ言語であり、Javaヒープに問い合わせて、そのJavaヒープから要求された情報のフィルタ処理/選択を可能にします。「クラスXのすべてのインスタンスを表示」というような事前定義の問合せはツールによってすでにサポートされていますが、OQLでは柔軟性が高くなっています。OQLは、JavaScript式言語に基づいています。
「ヒープ」ウィンドウでJavaヒープをロードする際、このウィンドウの「OQLコンソール」タブをクリックしてOQLエディタを開くことができます。OQLコンソールには、OQLエディタ、保存済OQL問合せウィンドウ、および問合せ結果を表示するウィンドウが含まれています。任意のサンプルOQL問合せを使用したり、ヒープ・データをフィルタ処理および選択する問合せを作成して、Javaヒープから必要な情報を特定することができます。問合せを選択または作成した後で、Javaヒープに対して問合せを実行して、結果を表示できます。
OQL問合せのフォームは次のとおりです。
select <JavaScript expression to select> [ from [instanceof] <class name> <identifier> [ where <JavaScript boolean expression to filter> ] ]
ここでは、class nameは完全修飾Javaクラス名(例: java.net.URL
)または配列クラス名、char[]
(または[C)はchar配列名、java.io.File
(または[Ljava.io.File;
)はjava.io.File[]
の名前というようになります。完全修飾されたクラス名が、実行時にJavaクラスを常に一意に識別するとはかぎりません。同名であっても、別のローダーによってロードされた複数のJavaクラスが存在している場合もあります。したがって、クラス名にはクラス・オブジェクトのID文字列が使用できます。instanceofキーワードが使用されている場合は、サブタイプ・オブジェクトが選択されます。このキーワードが指定されていない場合は、指定されたクラスのインスタンスのみが選択されます。from句とwhere句は両方ともオプションです。
select句および(オプションとして)where句では、式はJavaScript式で使用されます。フィールドに自然構文でアクセスできるようにするために、Javaヒープ・オブジェクトは便利なスクリプト・オブジェクトとしてラップされます。たとえば、Javaフィールドはobj.field_name
構文でアクセスでき、配列要素はarray[index]
構文でアクセスできます。選択されたJavaオブジェクトはそれぞれ、from句で指定された識別子名のJavaScript変数にバインドされます。
長さが100以上のStringをすべて選択します。
select s from java.lang.String s where s.count >= 100
長さが256以上のint配列をすべて選択します。
select a from int[] a where a.length >= 256
正規表現に一致するStringのコンテンツを表示します。
select {instance: s, content: s.toString()} from java.lang.String s where /java/(s.toString())
すべてのFileオブジェクトのパス値を表示します。
select file.path.toString() from java.io.File file
すべてのClassLoader
クラスの名前を表示します。
select classof(cl).name from instanceof java.lang.ClassLoader cl
指定されたID文字列によって識別されるClassのインスタンスを表示します。
select o from instanceof 0xd404b198 o
0xd404b198はClassのIDです(セッション内)。これを検索するには、そのクラスのページに表示されたIDを調べます。
ヒープ・オブジェクト
ヒープ組込みオブジェクトは、次のメソッドをサポートしています。
heap.forEachClass
: Javaクラスごとにコールバック関数をコールします。
heap.forEachClass(callback);
heap.forEachObject
: Javaオブジェクトごとにコールバック関数をコールします。
heap.forEachObject(callback, clazz, includeSubtypes);
clazz
は、インスタンスが選択されているクラスです。これが指定されない場合、java.lang.Object. includeSubtypes
のデフォルトは、サブタイプ・インスタンスを含めるかどうかを指定するブール・フラグになります。このフラグのデフォルト値はtrueです。
heap.findClass
: 指定された名前のJavaクラスを検索します。
heap.findClass(className);
この場合のclassName
は、検索するクラスの名前です。結果として得られるClassオブジェクトのプロパティは、次のとおりです。
name: クラスの名前。
superclass: スーパークラスのClassオブジェクト(または、java.lang.Object
の場合はnull)。
statics: Classの静的フィールドの名前と値のペア。
fields: フィールド・オブジェクトの配列。フィールド・オブジェクトのプロパティはnameとsignatureです。
loader: このクラスをロードしたClassLoaderオブジェクト。
Classオブジェクトのメソッドは、次のとおりです。
isSubclassOf
: 指定されたClassがこのクラスの直接または間接サブクラスかどうかをテストします。
isSuperclassOf
: 指定されたClassがこのクラスの直接または間接のスーパークラスかどうかをテストします。
subclasses
: 直接および間接サブクラスの配列を返します。
superclasses
: 直接および間接スーパークラスの配列を返します。
heap.findObject
: 指定されたオブジェクトIDからオブジェクトを検索します。
heap.findObject(stringIdOfObject);
heap.classes
: すべてのJavaクラスの列挙を返します。
heap.objects
: Javaオブジェクトの列挙を返します。
heap.objects(clazz, [includeSubtypes], [filter])
clazz
は、インスタンスが選択されているクラスです。これが指定されない場合、java.lang.Object. includeSubtypes
のデフォルトは、サブタイプ・インスタンスを含めるかどうかを指定するブール・フラグになります。このフラグのデフォルト値はtrueです。このメソッドでは、オプションのフィルタ式を受け入れてオブジェクトの結果セットをフィルタ処理します。
heap.finalizables
: - ファイナライズが保留中であるJavaオブジェクトの列挙を返します。
heap.livepaths
: - 指定されたオブジェクトがアライブしているパスの列挙を返します。このメソッドでは、ブール・フラグであるオプションの第2パラメータを受け入れます。このフラグにより、弱い参照のあるパスを含めるかどうかが指定されます。デフォルトでは、弱い参照のあるパスは含まれません。
select heap.livepaths(s) from java.lang.String s
この配列の各要素が、別の配列になっています。後の配列には、パスの「参照チェーン」に入っているオブジェクトが含まれています。
heap.roots
: - ヒープのRootの列挙が返されます。
各Rootオブジェクトのプロパティは、次のとおりです。
id: このルートにより参照されるオブジェクトの文字列ID
type: Rootの記述タイプ(JNIグローバル、JNIローカル、Java Staticなど)
description: Rootの文字列の記述
referrer: このルートに責任を持つ、またはnullであるThreadオブジェクトまたはClassオブジェクト
例
クラスjava.lang.System
の静的フィールド'props'にアクセスします。
select heap.findClass("java.lang.System").statics.props select heap.findClass("java.lang.System").props
java.lang.String
クラスのフィールド数を取得します。
select heap.findClass("java.lang.String").fields.length
オブジェクトIDが指定されているオブジェクトを検索します。
select heap.findObject("0xf3800b58")
名前パターンjava.net.*を持つクラスをすべて選択します。
select filter(heap.classes(), "/java.net./(it.name)")
個別のオブジェクトに対する関数
allocTrace
関数
指定されたJavaオブジェクトがある場合は、その割当てサイト・トレースを返します。allocTrace
はframeオブジェクトの配列を返します。各frameオブジェクトのプロパティは、次のとおりです。
className: メソッドがframeで実行中であるJavaクラスの名前。
methodName: - frameで実行中のJavaメソッドの名前。
methodSignature: frameで実行中のJavaメソッドのシグネチャ。
sourceFileName: frameで実行中のJavaクラスのソース・ファイルの名前。
lineNumber: メソッド内のソース行番号。
classof
関数
指定されたJavaオブジェクトのクラス・オブジェクトを返します。結果として得られるオブジェクトは次のプロパティをサポートしています。
name: クラスの名前。
superclass: スーパークラスのクラス・オブジェクト(または、java.lang.Object
の場合はnull)
statics: クラスの静的フィールドの名前と値のペア
fields: fieldオブジェクトの配列。fieldオブジェクトのプロパティはnameとsignatureです。
loader: このクラスをロードしたClassLoaderオブジェクト。
Classオブジェクトのメソッドは、次のとおりです。
isSubclassOf
: 指定されたクラスがこのクラスの直接または間接サブクラスかどうかをテストします。
isSuperclassOf
: 指定されたクラスがこのクラスの直接または間接のスーパークラスかどうかをテストします。
subclasses: 直接および間接サブクラスの配列を返します。
superclasses: 直接および間接スーパークラスの配列を返します。
例
各Referenceタイプ・オブジェクトのクラス名を表示します。
select classof(o).name from instanceof java.lang.ref.Reference o
java.io.InputStream
のすべてのサブクラスを表示します。
select heap.findClass("java.io.InputStream").subclasses()
java.io.BufferedInputStream
のすべてのスーパークラスを表示します。
show all superclasses of java.io.BufferedInputStream
forEachReferrer
関数
指定されたJavaオブジェクトのリファラごとにコールバック関数をコールします。
identical
関数
指定された2つのJavaオブジェクトが同一かどうかを返します。次に例を示します。
select identical(heap.findClass("Foo").statics.bar, heap.findClass("AnotherClass").statics.bar)
objectid
関数
指定されたJavaオブジェクトの文字列IDを返します。このIDはheap.findObject
に渡すことができ、識別のためにオブジェクトを比較する場合にも使用できます。次に例を示します。
select objectid(o) from java.lang.Object o
reachables
関数
指定されたJavaオブジェクトから推移的に参照されるJavaオブジェクトの配列を返します。オプションとして、カンマ区切りフィールド名である第2パラメータがアクセス可能性の計算から除外されることを受け入れます。フィールドはclass_name.field_name
パターンで作成されます。
例
各Propertiesインスタンスからアクセス可能なオブジェクトをすべて印刷します。
select reachables(p) from java.util.Properties p
各java.net.URL
からアクセス可能なオブジェクトをすべて印刷しますが、指定されたフィールド経由でアクセス可能なオブジェクトは省略されます。
select reachables(u, 'java.net.URL.handler') from java.net.URL u
referrers
関数
指定されたJavaオブジェクトの参照を保持するJavaオブジェクトの列挙を返します。このメソッドでは、ブール・フラグであるオプションの第2パラメータを受け入れます。このフラグにより、弱い参照を含めるかどうかが指定されます。デフォルトでは、弱い参照は含まれません。
例
java.lang.Object
インスタンスごとにリファラ数を印刷します。
select count(referrers(o)) from java.lang.Object o
java.io.File
オブジェクトごとにリファラを印刷します。
select referrers(f) from java.io.File f
2つ以上で参照された場合のみ、URLオブジェクトを印刷します。
select u from java.net.URL u where count(referrers(u)) > 2
referees
関数
指定されたJavaオブジェクトが直接参照するJavaオブジェクトの配列を返します。このメソッドでは、ブール・フラグであるオプションの第2パラメータを受け入れます。このフラグにより、弱い参照を含めるかどうかが指定されます。デフォルトでは、弱い参照は含まれません。たとえば、java.io.File
クラスのすべての静的参照フィールドを印刷するには、次のようにします。
select referees(heap.findClass("java.io.File"))
refers
関数
最初のJavaオブジェクトが2番目のJavaオブジェクトを参照するかどうかを返します。
root
関数
指定されたオブジェクトがオブジェクトのrootセットのメンバーである場合、この関数は、その理由を説明する説明Rootオブジェクトを返します。指定されたオブジェクトがrootではない場合、この関数はnullを返します。
sizeof
関数
指定されたJavaオブジェクトのサイズをバイト数で返します。次に例を示します。
select sizeof(o) from int[] o
retainedsize
関数
指定されたJavaオブジェクトの保持セットのサイズをバイト数で返します。注意: この関数をヒープ・ダンプに対してはじめて使用する場合は、かなり時間がかかる場合があります。
retainedsize
関数の使用例は次のとおりです。
select rsizeof(o) from instanceof java.lang.HashMap o
toHtml
関数
指定されたJavaオブジェクトのHTML文字列を返します。これは、select式によって選択されたオブジェクトに対して自動的にコールされます。ただし、より複雑な出力の印刷に役立つ場合があります。たとえば、ハイパーリンクを太字で印刷するには、次のようにします。
select "<b>" + toHtml(o) + "</b>" from java.lang.Object o
JavaScriptオブジェクトのリテラルまたは配列を使用して、複数の値を選択できます。
たとえば、スレッド・オブジェクトごとに名前とスレッドを表示します。
select { name: t.name? t.name.toString() : "null", thread: t } from instanceof java.lang.Thread t
配列/イテレータ/列挙操作関数
これらの関数は、配列/イテレータ/列挙および式文字列[または、コールバック関数]を入力として受け入れます。これらの関数は、配列/イテレータ/列挙を反復し、各要素に対して式(または関数)を適用します。注意: JavaScriptオブジェクトは連想配列です。したがって、これらの関数は任意のJavaScriptオブジェクトとともに使用することもできます。
concat
関数
指定された配列/列挙に、指定されたブール式がコードで指定した要素が含まれているかどうかを返します。評価されたコードは、次の組込み変数を参照できます。
it: 現在参照している要素
index: 現在の要素の索引
array: 反復中の配列/列挙
たとえば、一部の静的オブジェクトのいくつかのクラスによって参照されるPropertiesオブジェクトをすべて選択するには、次のようにします。
select p from java.util.Properties p where contains(referrers(p), "classof(it).name == 'java.lang.Class'")
count
関数
指定されたブール式を満たす入力配列/列挙の要素の件数を返します。ブール式のコードは、次の組込み変数を参照できます。
it: 現在参照している要素
index: 現在の要素の索引
array: 反復中の配列/列挙
たとえば、特定の名前パターンを持つクラスの数を印刷します。
select count(heap.classes(), "/java.io./(it.name)")
filter
関数
指定されたブール式を満たす入力配列/列挙の要素が含まれている配列/列挙を返します。ブール式のコードは、次の組込み変数を参照できます。
it: 現在参照している要素
index: 現在の要素の索引
array: 反復中の配列/列挙
result: 結果の配列/列挙
例
java.io.*
名前パターンを持つクラスをすべて表示します
select filter(heap.classes(), "/java.io./(it.name)")
リファラがjava.netパッケージに属していないURLオブジェクトのすべてのリファラを表示します。
select filter(referrers(u), "! /java.net./(classof(it).name)") from java.net.URL u
length
関数
配列/列挙の要素数を返します。
map
関数
指定されたコードを各要素で評価することにより、指定された配列/列挙を変換します。評価されたコードは、次の組込み変数を参照できます。
it: 現在参照している要素
index: 現在の要素の索引
array: 反復中の配列/列挙
result: 結果の配列/列挙
Map
関数は、入力配列/列挙の要素ごとにコードを繰り返しコールすることによって作成された値の配列/列挙を返します。
たとえば、次の名前と値を持つjava.io.File
の静的フィールドをすべて表示します。
select map(heap.findClass("java.io.File").statics, "index + '=' + toHtml(it)")
max
関数
指定された配列/列挙の最大要素を返します。オプションとして、配列の要素を比較するコード式を受け入れます。デフォルトでは、数値比較が使用されます。比較式では、次の組込み変数が使用できます。
lhs: 比較の左辺の要素
rhs: 比較の右辺の要素
例
最大長のstringインスタンスを検索します。
select max(map(heap.objects('java.lang.String', false), 'it.count'))
最大長であるstringインスタンスを検索します。
select max(heap.objects('java.lang.String'), 'lhs.count > rhs.count')
min
関数
指定された配列/列挙の最小要素を返します。オプションとして、配列の要素を比較するコード式を受け入れます。デフォルトでは、数値比較が使用されます。比較式では、次の組込み変数が使用できます。
lhs: 比較の左辺の要素
rhs: 比較の右辺の要素
例
最小サイズのvectorインスタンスを検索します。
select min(map(heap.objects('java.util.Vector', false), 'it.elementData.length'))
最大長であるvectorインスタンスを検索します。
select min(heap.objects('java.util.Vector'), 'lhs.elementData.length < rhs.elementData.length')
sort
関数
指定された配列/列挙をソートします。オプションとして、配列の要素を比較するコード式を受け入れます。デフォルトでは、数値比較が使用されます。比較式では、次の組込み変数が使用できます。
lhs: 比較の左辺の要素
rhs: 比較の右辺の要素
例
すべてのchar[]オブジェクトをサイズ順に印刷します。
select sort(heap.objects('char[]'), 'sizeof(lhs) - sizeof(rhs)')
すべてのchar[]オブジェクトをサイズ順に印刷しますが、サイズも併せて印刷します。
select map(sort(heap.objects('char[]'), 'sizeof(lhs) - sizeof(rhs)'), '{ size: sizeof(it), obj: it }')
top
関数
指定された配列/列挙の上位N個の要素を返します。オプションとして、配列の要素と上位要素の数を比較するコード式を受け入れます。デフォルトでは、外観の順序で上位10個の要素が返されます。比較式では、次の組込み変数が使用できます。
lhs: 比較の左辺の要素
rhs: 比較の右辺の要素
例
5つの最長文字列を印刷します
select top(heap.objects('java.lang.String'), 'rhs.count - lhs.count', 5)
5つの最長文字列を印刷しますが、サイズも併せて印刷します。
select map(top(heap.objects('java.lang.String'), 'rhs.count - lhs.count', 5), '{ length: it.count, obj: it }')
sum
関数
指定された入力配列または列挙のすべての要素の合計を返します。オプションとして、第2パラメータとして式を受け入れます。これは、入力要素を合計する前のマッピングに使用します。
たとえば、各Propertiesオブジェクトからアクセス可能なサイズの合計を返します。
select sum(map(reachables(p), 'sizeof(it)')) from java.util.Properties p // or omit the map as in ... select sum(reachables(p), 'sizeof(it)') from java.util.Properties p
toArray
関数
入力配列/列挙の要素が含まれている配列を返します。
unique
関数
指定された入力配列/列挙の一意の要素が含まれている配列/列挙を返します。
次の例では、文字列から参照される一意のchar[]インスタンスを選択しています。複数の文字列インスタンスでコンテンツに対して同じchar[]を共有できます。
// number of unique char[] instances referenced from any String select count(unique(map(heap.objects('java.lang.String'), 'it.value'))) // total number of Strings select count(heap.objects('java.lang.String'))
次の例では、各クラス・ローダーのヒストグラムと、それによってロードされたクラス数が印刷されます。
java.lang.ClassLoader
には、タイプjava.util.Vector
のクラスであるprivateフィールドがあり、Vectorにはベクター内の要素数であるelementCount
というprivateフィールドがあります。この問合せでは、JavaScriptオブジェクトのliteral関数およびmap関数を使用して複数の値(loader、count)を選択します。これは、sort関数を比較式とともに使用して、結果を件数(つまり、ロードされたクラス数)別にソートします。
select map(sort(map(heap.objects('java.lang.ClassLoader'), '{ loader: it, count: it.classes.elementCount }'), 'lhs.count < rhs.count'), 'toHtml(it) + "<br>"')
次の例では、クラス・ローダー・インスタンスごとに親子チェーンを示しています。
select map(heap.objects('java.lang.ClassLoader'), function (it) { var res = ''; while (it != null) { res += toHtml(it) + "->"; it = it.parent; } res += "null"; return res + "<br>"; })
java.lang.ClassLoader
クラスの親フィールドが使用されていて、コールをマップするためにコールバック関数を使用して親がnullになるまで検索していることに注意してください。
次の例では、すべてのSystemプロパティの値が印刷されます。この問合せ(および、その他の多くの問合せ)は、Javaプラットフォーム・クラスのprivateフィールドが通知(実装の詳細)なしに変更または削除された可能性があるため、安定していないことがあります。ただし、ユーザーのクラスを制御できる場合は、このような問合せをユーザーのクラスに使用しても安全です。
select map(filter(heap.findClass('java.lang.System').props.table, 'it != null && it.key != null && it.value != null'), function (it) { var res = it.key.toString() + ' = ' + it.value.toString(); return res; });
java.lang.System
には、タイプjava.util.Properties
の'props'という名前の静的フィールドがあります。
java.util.Properties
には、タイプjava.util.Hashtable$Entry
の'table'というフィールドがあります(このフィールドはjava.util.Hashtable
から継承されます)。これはハッシュ表バケット配列です。
java.util.Hashtable$Entry
にはキー、値、およびnextフィールドがあります。各エントリは、同じハッシュ表バケット内で次のエントリ(またはnull)を指します。
java.lang.String
クラスには、タイプchar[]のvalueフィールドがあります。
スナップショットでは、特定の時点のプロファイリング・データが取り込まれ、「スナップショット」ウィンドウを通じてアクセスできます。「スナップショットのアクセス」を参照してください
スナップショットは、次の点でプロファイリングのライブ結果とは異なります。
スナップショットは、プロファイリング・セッションが実行中でなくても確認できます。
スナップショットは容易に比較できます。
スナップショットを取得するためのオプションは次の2つです。
プロファイリング・セッションが進行している間。「プロファイリング・セッション中のスナップショットの取得」を参照してください
プロファイリング・セッションの最後。「プロファイリング・セッションの最後でのスナップショットの取得」を参照してください
プロファイリング・セッションの進行中に、プロファイルされたアプリケーションを閉じる場合、またはそれ自体が閉じる場合、プロファイラは「アプリケーション終了」ダイアログを表示することにより、その時点までに収集された結果のスナップショットを取得するかを尋ねます。
図10-8 「アプリケーション終了」ダイアログ
「はい」をクリックしてスナップショットを保存します。
次の図で示す「スナップショット」アイコンをクリックすることで、プロファイリング・データのスナップショットはプロファイリング・セッションのどの時点ででも取得できます。
図10-9 「スナップショット」アイコン
セッション中にスナップショット機能がどのように動作するかを制御するには、「ツール」→「プリファレンス」→「プロファイラ」の順に進み、「スナップショットの取得時」ドロップダウン・メニューをクリックして次のオプションを表示します。
新規スナップショットを開く—「スナップショット」アイコンをクリックした直後にスナップショットが開きます
新規スナップショットを保存—「スナップショット」アイコンをクリックするたびに新規スナップショットが保存されます
新規スナップショットを開いて保存—「スナップショット」アイコンをクリックした直後にスナップショットが保存されて開きます。
プロファイリング・セッション中に複数のスナップショットを取得することもでき、セッションの終わりにfinalスナップショットを保存するよう求められます。
「アプリケーション終了」ダイアログがプロファイリング・セッションの終わりに表示される際に、「今後このメッセージを表示しない」チェック・ボックスを選択すると、ダイアログは今後表示されなくなります。後でこのダイアログの表示を再度有効にする場合は、「ツール」→「プリファレンス」→「プロファイラ」に進み、次の図に示すように「リセット」ボタンをクリックします。
図10-10 プロファイラ・プリファレンス・ダイアログの「リセット」ボタン
IDEを使用してアプリケーションのプロファイリングを実行するには、事前にIDEを調整しておく必要があります。この調整プロセスは、プロファイリングに使用するJDKごとに実行する必要があります。これは、アプリケーションのバイトコード計測によってある程度のオーバーヘッドが生じ、より正確な結果を得るためには、コード計測にかかる時間を要因から取り除く必要があるために行います。
IDEの調整は、使用するJDKごとに1回だけで十分です。ただし、システムのパフォーマンスに影響するような変更をローカル構成またはリモート構成に加えた場合には、調整プロセスを再実行することをお薦めします。システム・パフォーマンスに影響を与える可能性のある変更とは、次のようなものです。
ハードウェアのアップグレード
オペレーティング・システムの有意な変更またはアップグレード
プロファイリングに使用するJavaプラットフォームのアップグレード
ローカル・システム用にIDEを調整する手順は次のとおりです。
「調整」をクリックすると、IDEでは選択したJavaプラットフォームで調整データが収集されます。調整プロセスが完了したら、IDEを使用してアプリケーションのプロファイリングを開始できます。
複数のコンピュータまたはシステム間で調整データを共有しないようにしてください。
プロファイリング・ポイントはソース・コード内のマーカーであり、特定のプロファイリング・アクションを起動できます。コードにプロファイリング・ポイントを設定するには、ソース・エディタのポップアップ・メニューを使用するか、「プロファイリング・ポイント」ウィンドウのツールバーを使用します。
次のタイプのプロファイリング・ポイントを設定できます。
結果のリセット
ストップウォッチ
スナップショットの取得
時間によるスナップショットの取得
トリガーによるスナップショットの取得
注意: 「時間によるスナップショットの取得」および「トリガーによるスナップショットの取得」のアイコンは、コード・エディタには表示されません。これらのアイコンは「プロファイリング・ポイント」ウィンドウにのみ表示されます。
プロファイリング・ポイントを使用して、プロファイリング結果のリセット、スナップショットの取得、またはコード・フラグメントのタイムスタンプまたは実行時間の記録を行うことができます。
プロファイリング・ポイントは、一度設定されると、削除されるまでプロジェクトに含まれます。
プロファイリング・ポイントを設定する手順は次のとおりです。
プロファイリング・ポイントを追加するクラスを特定して、ソース・エディタでそのクラスを開きます。
ソース・エディタで、プロファイリング・ポイントを追加する行の左余白を右クリックします。
「プロファイリング・ポイントの追加」を選択して、「新規プロファイリング・ポイント」ウィザードを開きます。
プロファイリング・ポイント・タイプとプロジェクトを選択します。
「次へ」をクリックします。
必要に応じて、プロファイリング・ポイントのプロパティをカスタマイズします。
「終了」をクリックします。
プロファイリング・ポイントのタイプを表すアイコンが、プロファイリング・ポイントを挿入したソース・エディタに表示されます。
プロファイリング・ポイントを有効または無効にするには、ソース・エディタで、プロファイリング・ポイントが含まれている行の左余白を右クリックして、<プロファイリング・ポイント名>→「有効化」または「無効化」を選択します。
アクティブなプロファイリング・ポイントを表示する手順は次のとおりです。
JUnitは、Java対応のリグレッション・テスト用オープン・ソース・フレームワークです。JUnitを使用して、Javaコードを検証するテストを記述および実行します。
テスト・フィクスチャ、テスト・ケースおよびテスト・スイートを作成するには、JDeveloperのJUnitウィザードを使用します。一般的なプロジェクト用のテスト・コンポーネントを作成するウィザードに加えて、ビジネス・コンポーネント・プロジェクトの専用ウィザードが提供されています。
JUnitは、JDeveloperにインストールおよび統合できるオプション機能です。
JUnitは、オプション機能で、JDeveloperとともに配布されません。使用する場合は、これをダウンロードおよびインストールする必要があります。
注意:
JUnitは、IBMのパブリック・ライセンス契約に基づいて提供されています。JUnitをダウンロードする前に、ライセンス契約を受け入れる必要があります。
JDeveloperにJUnitをインストールする手順は、次のとおりです。
JUnitテスト・アプリケーションは、次のコンポーネントで構成されます。
1つ以上のテスト・ケース。テスト対象のメソッドを呼び出し、予測される結果についてのアサーションを作成します。デフォルトで生成されるテスト・ケース・クラスは「Test」という名前ですが、ユーザーが任意の有効なJava名を指定できます。
テスト・フィクスチャ。テストが実行される状態を提供します。任意のクラスをテスト・フィクスチャとして使用できますが、JDeveloperには専用のテスト・フィクスチャ・クラスを作成するウィザードも用意されています。デフォルトで生成されるテスト・フィクスチャ・クラスは「Fixture」という名前ですが、ユーザーが任意の有効なJava名を指定できます。
テスト・スイート。テスト・ケースを呼び出します。デフォルトのテスト・スイート・クラスは、「AllTests」という名前です。
ランナー。テスト・スイートを呼び出し、テスト結果を相関付けて表示します。
テスト・フィクスチャとは、既知の値を持つオブジェクトのセットで、テスト・ケースにデータを提供します。任意のクラスをテスト・フィクスチャとして使用できますが、JDeveloperにはカスタムのテスト・フィクスチャ・クラスや様々な専用のテスト・フィクスチャ・クラスを作成できるウィザードも用意されています。
注意:
UnitTestFixture
はpublicクラスです。そのインスタンスを作成した場合、生成されたコードにエラーは生じません。
AppModuleAMFixture
はprivateクラスです。そのインスタンスを作成した場合、生成されたコードにはエラーが生じます。
JUnitのカスタムのテスト・フィクスチャ・クラスを作成するには、次のようにします。
テスト・フィクスチャとは、既知の値を持つオブジェクトのセットで、テスト・ケースにデータを提供します。JDBCテスト・フィクスチャは、使用するテスト・ケースにデータベース接続を確立するコードを提供します。
JUnitのJDBCテスト・フィクスチャ・クラスを作成するには、次のようにします。
「アプリケーション」ウィンドウで、プロジェクトを選択します。
「ファイル」→「新規」→「ギャラリから」を選択します。
「カテゴリ」ツリーで「一般」を開き、「ユニット・テスト」(JUnit)を選択します。
「項目」リストで、「テスト・フィクスチャ」をダブルクリックします。
ダイアログを完了するとテスト・フィクスチャ・クラスが作成されます。
作成されたクラスが、編集用に開かれます。
必要に応じてファイルを変更します。具体的には、テスト・フィクスチャ・オブジェクトを初期化するコードをsetUp()
メソッドに追加し、取得されたリソースを解放するコードをtearDown()
メソッドに追加します。
テスト・ケース・クラスには、JUnitのアサーションを呼び出してテストを実行する1つ以上のメソッドがあります。次の例は、JUnit 3.xでの典型的なテスト・ケースを示します。この例では、テスト対象のメソッドにテスト・フィクスチャ・データを渡してから、その結果を既知の値と比較し、予測どおりかどうかを確認しています。
public void testCountChars() { int expected = 4; int actual = fixture1.countChars('a'); assertEquals(expected, actual); }
@Test public void testCountChars() { int expected = 4; int actual = fixture1.countChars('a'); Assert.assertEquals(expected, actual); }
前述のテスト・ケースでは、countChars()
がテスト対象であり、テストの結果をassertEquals()
でチェックしています。これは、JUnit Assertクラスで定義されているアサーション・メソッドの一種です。テスト・フィクスチャfixture1
の状態はsetUp()
メソッドで確立され、このメソッドは次に示すようにテスト・ケースの呼出しより前に呼び出されていました。
protected void setup() throws Exception { fixture1 = new StringFixture("Goin' to Kansas City, Kansas City, here I come."); }
JUnitテスト・ケース・クラスを作成するには、次のようにします。
EJBアプリケーション専用のテスト・ケースを作成できます。詳細は、「JUnitを使用したEJBユニットのテスト方法」を参照してください。
メソッドに対するユニット・テストを、既存のJUnitテスト・ケース・クラスに追加できます。
JUnitテスト・ケース・クラスにテストを追加するには、次のようにします。
テスト・スイートは、テスト・ケースを呼び出すクラスです。
「JUnitテスト・スイート」ウィザードには、main()
メソッドと、TestRunnerクラスへのコールを挿入するオプションがあります。このオプションによってJDeveloperで「JUnit TestRunner」ログ・ウィンドウが開き、テスト結果が表示されます。別のテスト・ランナーを使用する場合には、メソッドを編集します。
次に示したJUnit 3.xテスト・スイートでは、suite()
メソッドによってTestSuiteインスタンスが作成され、それにテスト・ケースが追加されます。テスト・ケースを追加または削除する場合には、このメソッドを編集します。
public class AllTests { public static Test suite() { TestSuite suite; suite = new TestSuite("project1.AllTests"); return suite; }
次に示したJUnit 4テスト・スイートでは、テスト・ケース・クラスは@Suite
および@RunWith
の注釈を付けて記述されています。
@RunWith(Suite.class) @Suite.SuiteClasses( {}) public class AllTests1 { public static void main(String[] args) { String[] args2 = { AllTests1.class.getName() }; org.junit.runner.JUnitCore.main(args2); } }
JUnitテスト・スイート・クラスを作成するには、次のようにします。
JUnitテスト・ケースを作成する前に、テストされるプロジェクトの作成を完了している必要があります。
作成されるテキスト・フィクスチャは、接続数を減らすシングルトン・クラスです。テスト・ケースごとに接続または切断する場合は、JUnit 4の注釈、@Beforeおよび@After
を使用してテスト・ケースをカスタマイズします。
JUnit BC4Jテスト・スイート・ウィザードでは、アプリケーション・モジュールの各ビュー・オブジェクトに対してテストが生成されます。アプリケーション・モジュールがメソッドをエクスポートしていない場合、ウィザードによって、アプリケーション・モジュールそのもののテストも生成されます。生成されるビュー・オブジェクト・クラスの形式はview_objectVOTest.java
であり、形式がpackage.view.viewobjectVO
のパッケージに収められます。この場合のpackageはアプリケーション・モジュール・パッケージです。生成されるアプリケーション・モジュール・テストの形式はapplication_moduleAMTest.java
であり、形式がpackage.applicationModule
のパッケージに配置されます。生成されるテスト・フィクスチャ・クラスの形式はapplicationmoduleAMFixture.java
であり、アプリケーション・モジュール・テストと同じパッケージに配置されます。
生成されるすべてのテスト・スイート・クラスの形式はAllapplicationmoduleTest.java
であり、アプリケーション・モジュールのパッケージ名と同じ名前でパッケージに配置されます。
各アプリケーション・モジュールまたはビュー・オブジェクト・テストについてテスト・ケースのXMLファイルも生成されます。このXMLファイルには、アプリケーション・モジュールまたはビュー・オブジェクト・テスト・ケースで定義されているテスト・メソッドが含まれます。重複が多くなりすぎる可能性があるため、ベース・クラス(ある場合)のテスト・メソッドは含まれません。
ビジネス・コンポーネント・テスト・スイートを作成するには:
ビジネス・コンポーネントのテスト・スイートを作成すると、ビジネス・コンポーネントのテスト・フィクスチャも一緒に作成されます。また、ビジネス・コンポーネントのテスト・フィクスチャを個別に作成することもできます。
生成されるテスト・フィクスチャ・クラスの形式はapplicationmoduleAMFixture.java
であり、形式がpackage.applicationModule
のパッケージに配置されます。この場合のpackageはアプリケーション・モジュール・パッケージです。
ビジネス・コンポーネント・テスト・フィクスチャを作成するには:
プロジェクトのすべてのテスト・ケースでテスト・スイートを更新します。
テスト・スイートを更新するには、次のようにします。
suite()
メソッドを持つクラスで、コンテキスト・メニューから「ソース」→「テスト・スイートのリフレッシュ」を選択します。