Java Platform Standard Edition

トラブルシューティング・ガイド

リリース9

E90916-02(原本部品番号:E61074-05)

2017年10月

このガイドは、Java Platform, Standard Edition (Java SE)およびJava HotSpot VMで作成されたJavaクライアント・アプリケーションで発生する可能性のある問題のトラブルシューティングに役立ちます。

Java Platform, Standard Editionトラブルシューティング・ガイド, リリース9

E90916-02

Copyright © 1995, 2017, Oracle and/or its affiliates.  All rights reserved.

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

ここに記載された情報は予告なしに変更される場合があります。また、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクル社までご連絡ください。

このソフトウェアまたは関連ドキュメントを、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供する場合は、次の通知が適用されます。

U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are "commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, use, duplication, disclosure, modification, and adaptation of the programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, shall be subject to license terms and license restrictions applicable to the programs. No other rights are granted to the U.S. Government.

このソフトウェアまたはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアまたはハードウェアは、危険が伴うアプリケーション(人的傷害を発生させる可能性があるアプリケーションを含む)への用途を目的として開発されていません。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用する際、このソフトウェアまたはハードウェアを安全に使用するために、適切な安全装置、バックアップ、冗長性(redundancy)、その他の対策を講じることは使用者の責任となります。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、オラクル社およびその関連会社は一切の責任を負いかねます。

OracleおよびJavaはOracle Corporationおよびその関連企業の登録商標です。その他の名称は、それぞれの所有者の商標または登録商標です。

Intel、Intel Xeonは、Intel Corporationの商標または登録商標です。すべてのSPARCの商標はライセンスをもとに使用し、SPARC International, Inc.の商標または登録商標です。AMD、Opteron、AMDロゴ、AMD Opteronロゴは、Advanced Micro Devices, Inc.の商標または登録商標です。UNIXは、The Open Groupの登録商標です。

このソフトウェアまたはハードウェア、そしてドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。適用されるお客様とOracle Corporationとの間の契約に別段の定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。適用されるお客様とOracle Corporationとの間の契約に定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても一切の責任を負いかねます。

目次

タイトルおよび著作権情報

はじめに

- 対象読者

- ドキュメントのアクセシビリティについて

- 関連ドキュメント

- 表記規則

第I部 Javaの一般的なトラブルシューティング

1 トラブルシューティングのためのJavaの準備

- トラブルシューティングのためのJavaの設定

- JVMのトラブルシューティングのためのオプションおよびフラグの有効化

- 関連データの収集

- デバッグが容易なJavaアプリケーションの作成

2 診断ツール

- 診断ツールの概要

- Java Mission Control

- Java Mission Controlによるトラブルシューティング

- Javaフライト記録とは

- 記録の種類

- フライト記録の作成方法

- Java Mission Controlによるフライト記録の作成

- コマンド行での起動フラグによるフライト記録の作成

- トリガーによる自動記録

- フライト記録の検査

- 検査のためのサンプルJFRの取得方法

- 範囲ナビゲータ

- 「全般」タブ

- 「メモリー」タブ

- 「コード」タブ

- 「スレッド」タブ

- 「I/O」タブ

- 「システム」タブ

- 「イベント」タブ

- jcmdユーティリティ

- jcmdユーティリティの有用なコマンド

- jcmdコマンド・ユーティリティを使用したトラブルシューティング

- ネイティブ・メモリー・トラッキング

- NMTによるメモリー・リークの検出

- VM内部メモリーをモニターする方法

- JConsole

- JConsoleツールによるトラブルシューティング

- JConsoleによるローカルおよびリモート・アプリケーションのモニター

- jdbユーティリティ

- jdbユーティリティによるトラブルシューティング

- プロセスの接続

- 同一マシン上のコア・ファイルへの接続

- 別のマシンからのコア・ファイルまたはハング・プロセスへの接続

- jinfoユーティリティ

- jinfoユーティリティによるトラブルシューティング

- jmapユーティリティ

- ヒープの構成と使用量

- ヒープ・ヒストグラム

- Permanent世代の統計

- jpsユーティリティ

- jstackユーティリティ

- jstackユーティリティによるトラブルシューティング

- コア・ダンプからのスタック・トレース

- 混合スタック

- jstatユーティリティ

- visualgcツール

- Ctrl+Breakハンドラ

- スレッド・ダンプ

- デッドロックの検出

- ヒープ・サマリー

- オペレーティング・システムのネイティブ・ツール

- DTraceツール

- Java HotSpot VMのプローブ・プロバイダ

- pmapユーティリティの改善

- pstackユーティリティの改善

- カスタム診断ツール

- Java Platform Debugger Architecture

- NMTのメモリー・カテゴリ

- ポストモーテム診断ツール

- ハングアップ・プロセス・ツール

- モニタリング・ツール

- その他のツール、オプション、変数、およびプロパティ

- java.lang.managementパッケージ

- java.lang.instrumentパッケージ

- java.lang.Threadクラス

- JVM Tool Interface

- jrunscriptユーティリティ

- jsadebugdデーモン

- jstatdデーモン

- スレッド・ダンプのスレッド状態

- オペレーティング・システムに基づくトラブルシューティング・ツール

3 メモリー・リークのトラブルシューティング

- Javaフライト・レコーダを使用したメモリー・リークのデバッグ

- メモリー・リークの検出

- リーク・クラスの調査

- リークの調査

- OutOfMemoryError例外の理解

- OutOfMemoryErrorのかわりにクラッシュのトラブルシューティング

- Java言語コードでのリークの診断

- ヒープ・ヒストグラムの取得

- ファイナライズを保留中のオブジェクトのモニター

- ネイティブ・コードでのリークの診断

- すべてのメモリー割当ておよび解放呼出しの追跡

- JNIライブラリのすべてのメモリー割当ての追跡

- オペレーティング・システム・サポートによるメモリー割当ての追跡

- dbxデバッガによるリークの検出

- libumemツールによるリークの検出

4 JFRを使用したパフォーマンスの問題のトラブルシューティング

- JFRのオーバーヘッド

- ボトルネックの検出

- ガベージ・コレクションのパフォーマンス

- 同期パフォーマンス

- I/Oパフォーマンス

- コード実行パフォーマンス

第II部 JVMの問題のデバッグ

5 システム・クラッシュのトラブルシューティング

- クラッシュの発生場所の特定

- ネイティブ・コードでのクラッシュ

- コンパイル済コードでのクラッシュ

- HotSpotコンパイラ・スレッドでのクラッシュ

- VMスレッドでのクラッシュ

- スタック・オーバーフローによるクラッシュ

- 回避方法の特定

- HotSpotコンパイラ・スレッドまたはコンパイル済みコードでのクラッシュの回避

- ガベージ・コレクション中のクラッシュの回避

- クラス・データ共有によって発生するクラッシュの回避

- Microsoft Visual C++のバージョンに関する考慮事項

6 プロセスのハングおよびループのトラブルシューティング

- ループ処理の診断

- ハング・プロセスの診断

- デッドロックが検出された

- デッドロックが検出されなかった

- スレッド・ダンプがない

- Oracle Solaris 8スレッド・ライブラリ

7 シグナルおよび例外の処理

- Oracle Solaris、LinuxおよびmacOSでのシグナルの処理

- Windowsでの例外処理

- シグナル・チェーン

- Java HotSpot VMによる例外処理

- コンソール・ハンドラ

- Oracle Solaris、LinuxおよびmacOSで使用されるシグナル

第III部 コア・ライブラリの問題のデバッグ

8 JREでのタイムゾーン設定

- ネイティブ・タイムゾーン情報およびJRE

- 使用中のタイムゾーン・データ・バージョンの判別

- TZupdaterの問題のトラブルシューティング

- Windowsでのデフォルト・タイムゾーンの判別

- JREが報告するデフォルト・タイムゾーンの確認

- コントロール・パネルでの設定の確認

- 夏時間の自動調整の確認

- コントロール・パネルでのデフォルト・タイムゾーンの設定

- -Duser.timezoneシステム・プロパティの確認

- Windows 7の特別なツール

- タイムゾーン・マッピングのJRE内部表現

第IV部 クライアントの問題のデバッグ

9 クライアントの問題の概要

- Java SE Desktopテクノロジ

- 一般的な問題のトラブルシューティング手順

- 問題のタイプの特定

- Javaクライアントのクラッシュ

- パフォーマンスの問題

- 動作の問題

- 基本ツール

- Java Debug Wire Protocol

10 AWT

- AWTのデバッグに関するヒント

- レイアウト・マネージャの問題

- キー・イベント

- モーダリティの問題

- AWTのクラッシュ

- フォーカス・イベント

- フォーカス・イベントをトレースする方法

- ネイティブ・フォーカス・システム

- Java Plug-inでのフォーカス・システム

- Xウィンドウ・マネージャでサポートされているフォーカス・モデル

- フォーカスのその他の問題

- データ転送

- ドラッグ・アンド・ドロップ・アプリケーションのデバッグ

- データ転送に関するよくある問題

- その他の問題

- スプラッシュ画面の問題

- トレイ・アイコンの問題

- ポップアップ・メニューの問題

- 背景色または前景色の継承

- AWTパネルのサイズ制限

- X11上でポップアップ・メニューや類似コンポーネントのデバッグ中にハングアップする

- X11でのWindow.toFront()/toBack()の動作

- 重量または軽量コンポーネントの混在

11 Java 2Dパイプラインのレンダリングおよびプロパティ

- Oracle SolarisおよびLinux: X11パイプライン

- X11パイプラインのピックスマップのプロパティ

- X11パイプラインのMIT共有メモリー拡張

- SPARC上のOracle Solaris: DGAのサポート

- SPARC上のOracle Solaris - Java 2Dのデフォルト・ビジュアルの変更

- Windows OS - DirectDraw/GDIパイプライン

- Windows OS - 全画面モードでのDirect3Dパイプライン

- Oracle Solaris、LinuxおよびWindowsでのOpenGLパイプライン

- OpenGLパイプラインの有効化

- 最小要件

- 起動の問題の診断

- レンダリングおよびパフォーマンスの問題の診断

- 最新のOpenGLドライバ

12 Java 2D

- 一般的なパフォーマンス問題

- ハードウェア高速化レンダリング・プリミティブ

- 非高速化レンダリングの検出および回避のためのプリミティブ・トレース

- 低いレンダリング・パフォーマンスの原因

- ソフトウェアのみのレンダリングのパフォーマンスの改善

- テキスト関連の問題

- テキスト・レンダリング中のアプリケーション・クラッシュ

- テキストの外観の違い

- メトリック

- Java 2D印刷

13 Swing

- Swingの一般的なデバッグ・ヒント

- Swingの具体的なデバッグ・ヒント

- 不正なスレッド処理

- JComponentの子のオーバーラップ

- 表示の更新

- モデルの変更

- コンポーネントの追加または削除

- 不透明のオーバーライド

- グラフィックへの永続的な変更

- カスタム・ペイントとダブル・バッファリング

- 不透明なコンテンツ・ペイン

- 各セルのレンダラ呼出しのパフォーマンス

- リークの可能性

- 重量コンポーネントと軽量コンポーネントの混在

- Synthの使用

- イベント・ディスパッチ・スレッド上のアクティビティの追跡

- デフォルト・レイアウト・マネージャの指定

- リスナー・オブジェクトの不適切なコンポーネントへのディスパッチ

- コンテンツ・ペインへのコンポーネントの追加

- ドラッグ・アンド・ドロップのサポート

- コンポーネントの親は1つ

- JFileChooserとWindowsショートカットの問題

14 国際化

- 国際化およびローカリゼーションのトラブルシューティング

15 Javaサウンド

- Javaサウンドの問題のトラブルシューティング

16 アプレットとJava Web Startアプリケーション

- 構成の問題

- 検証

- 一般的な構成の問題

- Javaランタイムの管理

- JREへのパラメータの受渡し

- Javaデプロイメント・ホーム

- デプロイメントのトレース

- デプロイメント・キャッシュ

- ネットワーク構成

- アプレットのトラブルシューティング

- アプレットを起動するためのプラグインのチート・シート

- ブラウザまたはJavaプロセスのクラッシュ

- 応答のないWebページ

- セキュリティ・ダイアログ・ボックスの回避

- 署名付きのアプリケーション

- 混合コードの問題

- 開発のヒント

第V部 バグ・レポートの提出

17 バグ・レポートの提出

- 更新リリースに含まれる修正のチェック

- バグ・レポートの提出の準備

- バグ・レポート用のデータの収集

- ハードウェアの詳細

- オペレーティング・システムの詳細

- Java SEのバージョン

- コマンド行オプション

- 環境変数

- 致命的エラー・ログ

- コア・ダンプおよびクラッシュ・ダンプ

- 問題の詳細な説明

- ログおよびトレース

- トラブルシューティング手順からの結果

- コア・ダンプの収集

- Oracle Solarisでのコア・ダンプの収集

- Linuxでのコア・ダンプの収集

- コア・ファイルを取得しない理由

- Windowsでのクラッシュ・ダンプの収集

付録

A 致命的エラー・ログ

- 致命的エラー・ログの場所

- 致命的エラー・ログの説明

- ヘッダー形式

- スレッド・セクションの形式

- プロセス・セクションの形式

- システム・セクションの形式

B Java 2Dのプロパティ

- Oracle SolarisおよびLinuxでのプロパティ

- Windows上のプロパティ

C 環境変数とシステム・プロパティ

- JAVA_HOME環境変数

- JAVA_TOOL_OPTIONS環境変数

- java.security.debugシステム・プロパティ

D コマンド行オプション

- Java HotSpot VMコマンド行オプション

- その他のコマンド行オプション

E このリリースのツールのサマリー

はじめに

このドキュメントは、Java Platform, Standard Edition (Java SE)およびJava HotSpot VMで作成されたJavaクライアント・アプリケーションで発生する可能性のある問題のトラブルシューティングに役立ちます。このドキュメントでは、問題を分析する際に役立つ可能性のある使用可能なツールおよびコマンド行オプションについて説明します。さらに、このドキュメントではコア・ライブラリとクライアントの問題をデバッグするための指針を示し、クラッシュ、ハングアップ、メモリー・リークなどのいくつかの一般的な問題についても説明します。最後に、このドキュメントではデータの収集とバグ・レポートの準備に関する方針を示します。

対象読者

このドキュメントの対象読者は、Java Development Kit (JDK)を使用する開発者です(JDKとはOracleによるJava Platform, Standard Edition (Java SE)の実装のこと)。最新のリリースはJava SE 9およびJDK 9ですが、このドキュメントに記載された情報のほとんどは以前のリリースにも適用できます。

このドキュメントは、Javaクライアント・テクノロジを詳しく理解し、Java HotSpot VMのコンポーネントの概略を理解し、加えてガベージ・コレクション、スレッド、ネイティブ・ライブラリなどの概念についてもある程度理解している読者を対象としています。さらに、Javaアプリケーションが開発および実行されるオペレーティング・システムについて読者が適度に習熟しているものと想定します。

ドキュメントのアクセシビリティについて

Oracleのアクセシビリティについての詳細情報は、Oracle Accessibility ProgramのWebサイト(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc)を参照してください。

Oracle Supportへのアクセス

サポートをご購入のOracleのお客様は、My Oracle Supportにアクセスして電子サポートを受けることができます。詳細情報は(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info) か、聴覚に障害のあるお客様は (http://www.oracle.com/pls/topic/lookup?ctx=acc&id=trs)を参照してください。

関連ドキュメント

Java SEおよび関連するクライアント/デスクトップ・テクノロジの詳細は、Java SEホームにアクセスしてください。

表記規則

このマニュアルでは次の表記規則を使用します。

規則

意味

太字

太字は、操作に関連するGraphical User Interface要素、または本文中で定義されている用語および用語集に記載されている用語を示します。

イタリック体

イタリックは、ユーザーが特定の値を指定するプレースホルダ変数を示します。

固定幅フォント

固定幅フォントは、段落内のコマンド、URL、サンプル内のコード、画面に表示されるテキスト、または入力するテキストを示します。

第I部 Javaの一般的なトラブルシューティング

様々な診断ツールやモニタリング・ツール、メモリー・リークの診断、パフォーマンス問題の識別に対するJavaのトラブルシューティング手法。

この部では、Javaの一般的なトラブルシューティング方法について説明します。内容は次のとおりです。

- トラブルシューティングのためのJavaの準備
より良いトラブルシューティング手法のためのJavaとJavaアプリケーションの設定のためのガイドラインを提供します。これらの事前のJavaの設定は、JavaおよびJavaアプリケーションの問題のデバッグおよび絞込みに役立ちます。

- 診断ツール
Java Development Kit (JDK)で使用される各種の診断およびモニタリング・ツールについて説明します。さらに、JDK 9で使用可能なトラブルシューティング・ツールと、アプリケーション・プログラミング・インタフェース(API)を使用したカスタム・ツールの開発についても説明します。

- メモリー・リークのトラブルシューティング
メモリー・リークが関与する問題の診断についてアドバイスを行います。

- JFRを使用したパフォーマンスの問題のトラブルシューティング
Javaアプリケーションのパフォーマンスの問題を識別し、Javaフライト・レコーダを使用して問題をデバッグします。

1 トラブルシューティングのためのJavaの準備

この章では、よりよいトラブルシューティング手法のためのJavaとJavaアプリケーションの設定指針について説明します。これらの事前のJavaの設定は、Javaおよびアプリケーションの問題のデバッグおよび絞込みに役立ちます。すべての提案がすべてのアプリケーションに適用されるものではありません。

この章の構成は、次のとおりです。

- トラブルシューティングのためのJavaの設定

- JVMのトラブルシューティングのためのオプションおよびフラグの有効化

- 関連データの収集

トラブルシューティングのためのJavaの設定

トラブルシューティングの関連データを収集可能にするためのJava環境およびコマンド行オプションを設定します。

Javaを設定する手順は次のとおりです。

1. Javaのバージョンの更新: 修正されたJavaの問題のトラブルシューティングに時間をかけることを避けるために、最新のJavaバージョンを使用します。多くの場合、Javaランタイムのバグに起因する問題は、最新のアップデート・リリースで修正されています。最新のJavaバージョンの使用により、いくつかの一般的な既知の問題を回避できます。

2. デバッグのためのJava環境の設定: 大きなJavaアプリケーションの設定、ランチャ・スクリプトによるアプリケーションの起動、または複数のマシンで分散Javaを実行するときは次のシナリオを検討してください。

a. Javaバージョンを簡単に変更できるようにする: Javaの最新バージョンにより、多くの実行時の問題を回避できます。スクリプトを実行してアプリケーションを起動する場合は、Javaパスの更新が必要となる場所が1箇所のみであることを確認してください。分散システムで実行する場合は、すべてのマシンのJavaバージョンが変更される簡単な方法を考えます。

b. Javaのコマンド行オプションを簡単に変更できるようにする: たとえば、詳細出力の追加、機能をオフにする、パフォーマンスの向上のためのJavaのチューニングなどのために、トラブルシューティング中にJavaオプションを変更する場合があります。これらの変更に備えてシステムを準備しておきます。
テスト・フレームワークやクラウド・ソリューションなどでJavaアプリケーションをリモートで実行する場合にも、Javaフラグを簡単に変更できることを確認してください。問題を再現するために、アプリケーションにコマンド行パラメータを指定したり、フラグをすぐに試してみたい場合があります。これらの変更が簡単に行えるようにシステムを準備します。

JVMのトラブルシューティングのためのオプションおよびフラグの有効化

トラブルシューティングの関連データを収集可能にするためのJVMオプションやフラグを設定します。

収集するデータはシステムごとに異なり、問題が発生した場合に使用するデータによっても異なります。次のデータの収集を検討します。

1. コア・ファイルを有効にする: たとえば、セグメンテーション・フォルトによってJavaがクラッシュした場合、OSはディスクにコア・ファイル(メモリーの完全なダンプ)を保存します。LinuxおよびSolarisでは、コア・ファイルがデフォルトで無効になっている場合があります。Linux/Solaris上のコア・ファイルを有効にするには、通常、アプリケーション・コマンドを起動する前にulimit -c unlimitedを実行するだけで十分です。一部のシステムでは、これらの制限の扱い方が異なる場合があります。

注意:
コア・ファイルは、特に大きなJavaヒープで実行する場合、多くのディスク領域を取ります。

コア・ファイルを有効にするかどうかを決定するときは、システムでクラッシュが発生した場合の対処方法を検討します。コア・ファイルを参照したいと思いますか。多くのJavaユーザーには、コア・ファイルは大して役に立ちません。ただし、クラッシュが発生した場合に、gdbなどのネイティブ・デバッガまたはServiceability Agentを使用してデバッグする場合は、アプリケーションを起動する前にコア・ファイルを有効にしてください。

多くの場合、クラッシュを再現するのは困難です。そのため、アプリケーションを起動する前にコア・ファイルを有効にします。

2. JVMフラグに-XX:+HeapDumpOnOutOfMemoryErrorを追加する: -XX:+HeapDumpOnOutOfMemoryErrorフラグは、アプリケーションでOutOfMemoryErrorが発生した場合に、ディスクにJavaヒープのダンプを保存します。

ヒープ・ダンプは、特に大きなJavaヒープで実行する場合、コア・ファイルと同様に非常に大きくなる可能性があります。

再び、アプリケーションでOutOfMemoryErrorが発生した場合の対処方法を考えます。エラーの発生時に、ヒープを検査したいと思いますか。その場合、アプリケーションで予想外のOutOfMemoryErrorが発生したときにこのデータを取得するために、フラグをデフォルトで設定します。

3. Javaフライトの連続記録を実行する: Java Flight Recorder (JFR)は商用機能です。開発者のデスクトップおよびラップトップ上では無償で、また評価目的であればテスト環境、開発環境および本番環境で使用できます。ただし、JFRを本番サーバーで有効にする場合は、商用ライセンスが必要です。

連続フライト記録を実行するためにJavaを設定します。連続フライト記録は、JFRイベントの循環バッファです。アプリケーションで問題が発生した場合は、最後の1時間の実行からデータをダンプできます。JFRイベントは、メモリー・リーク、ネットワーク・エラー、高いCPU使用率、スレッドのブロックなど、幅広い問題のデバッグに役立ちます。

連続フライト記録を使用した実行のオーバーヘッドは非常に低いです。Java連続フライト記録の作成の詳細は、「フライト記録の作成方法」を参照してください。

4. JVMコマンド行に-verbosegcを追加する: -verbosegcフラグは、Javaガベージ・コレクタの基本情報をログします。このログは、次のことを見つけるのに役立ちます。

- ガベージ・コレクションの実行に時間がかかっているかどうか。

- 空きメモリーが経時的に減少しているかどうか。

ガベージ・コレクタのログは、アプリケーションがOutOFMemoryErrorをスローするか、パフォーマンスの問題が発生した場合、問題の診断に役立ちます。したがって、-verbosegcフラグをデフォルトでオンにすると、問題のトラブルシューティングに役立ちます。

注意:
アプリケーションの再起動によって前のログが削除されないように、ログ・ローテーションを使用します。JDK7以降では、UseGClogFileRotationフラグおよびNumberOfGCLogFilesフラグをログ・ローテーションの設定に使用できます。これらのフラグの詳細は、Java HotSpot VMのデバッグ・オプションに関する項を参照してください。

5. JavaバージョンおよびJVMフラグを出力する: Javaのバグを提出したりフォーラムで助けを求める前に、ログ・ファイルの基本情報がすぐわかるようにしておきます。たとえば、Javaバージョンと使用しているJVMフラグを印刷すると役立ちます。

アプリケーションをスクリプトで起動する場合は、実行する前に、java -versionを実行してJavaバージョンを印刷し、コマンド行を印刷してください。または、JVMの引数に-XX+PrintCommandLineFlagsと-showversionを追加することもできます。

6. リモート・モニタリングのためのJMC JMXの設定: JMXは、Mission ControlまたはVisual VMなどのツールを使用してJavaアプリケーションにリモート接続するために使用できます。アプリケーションを実行しているマシン上でこれらのツールを実行できる場合を除き、これを設定すると、後のアプリケーションのモニター、診断コマンドの送信、フライト記録の管理などに役立ちます。JMXを有効化した場合、パフォーマンス上のオーバーヘッドはありません。

別の方法は、Javaアプリケーションの起動後にJMXを有効にする方法で、ManagementAgent.start診断コマンドを使用します。コマンドで送信できるフラグの一覧については、jcmd <pid> help ManagementAgent.startを実行してください。

「jcmdユーティリティ」を参照してください。

関連データの収集

アプリケーションで問題が発生した場合、問題の詳細をデバッグするには、特に再起動により前のファイルが削除される場合は、システムを再起動する前に関連データを収集してください。

- 次のファイルを収集することが重要です。

- クラッシュの問題に関するコア・ファイル。

- Javaのクラッシュの場合に出力されるhs_errテキスト・ファイル。

- ログ・ファイル: Javaおよびアプリケーションのログ。

- -XX:+HeapDumpOnOutOfMemoryErrorのJavaヒープ・ダンプ。

- Javaフライト記録(有効な場合)。問題によってアプリケーションが終了しなかった場合は、連続記録をダンプします。

- アプリケーションが応答しない場合は、次のファイルを収集します。

- スタック・トレース: システムを再起動する前に、jcmd <pid> Thread.printを使用して複数のスタック・トレースを取ります。

- フライト記録をダンプします(有効な場合)。

- コア・ダンプの強制: アプリケーションを正常に閉じることができない場合は、LinuxまたはSolarisシステムで、アプリケーションを停止し、kill -6 <pid>を使用してコア・ファイルを強制します。

デバッグが容易なJavaアプリケーションの作成

ロギング・フレームワークの使用は、後のデバッグを可能にするための良い方法です。

特定のモジュールで問題が発生する場合は、そのモジュールのロギングを有効にできる必要があります。また、情報、デバッグ、トレースなどの異なるロギング・レベルの指定も役立ちます。

2 診断ツール

この章では、Java Development Kit (JDK)で使用可能な診断および他のモニタリング・ツールを紹介します。その後で、JDK 9の診断ツールおよび各種オペレーティング・システム固有のトラブルシューティング・ツールについて詳しく説明します。この章の最後では、JDKが提供するアプリケーション・プログラミング・インタフェース(API)を使用してカスタム診断ツールを開発する方法について説明します。

この章の構成は、次のとおりです。

- 診断ツールの概要

- Javaフライト記録とは

- フライト記録の作成方法

- フライト記録の検査

- jcmdユーティリティ

- ネイティブ・メモリー・トラッキング

- JConsole

- jdbユーティリティ

- jinfoユーティリティ

- jmapユーティリティ

- jpsユーティリティ

- jrunscriptユーティリティ

- jstackユーティリティ

- jstatユーティリティ

- visualgcツール

- Ctrl+Breakハンドラ

- オペレーティング・システムのネイティブ・ツール

- カスタム診断ツール

- jsadebugdデーモン

- jstatdデーモン

診断ツールの概要

この項で説明するコマンド行ユーティリティのほとんどは、JDKに含まれているか、オペレーティング・システムのネイティブなツールやユーティリティです。

JDKコマンド行ユーティリティはJDKのダウンロードに含まれていますが、Java Runtime Environment (JRE)にデプロイされたアプリケーションの問題の診断やモニターのためにそれらを使用できるかどうかを検討するのは重要なことです。

一般に、診断ツールやオプションは、様々なメカニズムを使用して報告対象の情報を取得します。それらのメカニズムは仮想マシン(VM)実装、オペレーティング・システムおよびリリースに固有のものです。通常、ツールの一部のみが、特定の時点で特定の問題に適用可能です。「-XX」という接頭辞が付いたコマンド行オプションは、Java HotSpot VMに固有のものです。「Java HotSpot VMコマンド行オプション」を参照してください。

注意:

-XXオプションはJava APIの一部ではなく、リリースごとに変わる可能性があります。

これらのツールおよびオプションは、トラブルシューティングの対象となっている問題のタイプに応じて、いくつかのカテゴリに分けられます。場合によっては、1つ以上のカテゴリに分けられるツールやオプションもあります。

- ポストモーテム診断 これらのツールおよびオプションを使用すれば、アプリケーションがクラッシュした後に問題を診断できます。「ポストモーテム診断ツール」を参照してください。

- ハングアップ・プロセス これらのツールを使用して、ハング・アップまたはデッドロックしたプロセスを調査できます。「ハングアップ・プロセス・ツール」を参照してください。

- モニタリング これらのツールを使用して、実行中のアプリケーションをモニターできます。「モニタリング・ツール」を参照してください。

- その他 これらのツールやオプションを使用すれば、その他の問題を診断しやすくなります。「その他のツール、オプション、変数、およびプロパティ」を参照してください。

注意:

この項で説明するコマンド行ユーティリティのいくつかは、試験的なものです。jstack、jinfo、およびjmapユーティリティが、試験的なユーティリティの例です。以前のjstack、jinfoおよびjmapユーティリティのかわりに最新の診断ツールjcmdの使用をお薦めします。

Java Mission Control

Java Mission Control (JMC)は、HotSpot JVM用の新しいJDKプロファイリングおよび診断ツール・プラットフォームです。

基本的なモニタリングと管理、および本番稼働時のプロファイリングと診断を行う高性能なツール・スイートです。Java Mission Controlは、プロファイリング・ツールにつきもののパフォーマンス・オーバーヘッドの問題を最小限に抑えます。このツールはJVMに組み込まれた商用機能であり、実行時に使用できます。

Java Flight Recorder (JFR)は商用機能です。開発者のデスクトップおよびラップトップ上では無償で、また評価目的であればテスト環境、開発環境および本番環境で使用できます。ただし、JFRを本番サーバーで有効にする場合は、商用ライセンスが必要です。それ以外の目的でJMC UIをJDK上で使用する場合、商用ライセンスは必要ありません。

Java Mission Control (JMC)は、Java管理コンソール(JMX)とJava Flight Recorder (JFR)の他、ツールからダウンロード可能ないくつかのプラグインで構成されます。JMXはJavaアプリケーションのモニタリングおよび管理用ツールで、JFRはプロファイリング・ツールです。Java Mission Controlは、Eclipse IDE用のプラグインのセットとしても使用可能です。

次のトピックでは、Java Mission Controlでトラブルシューティングを行う方法を説明します。

- Java Mission Controlによるトラブルシューティング

Java Mission Controlによるトラブルシューティング

Java Mission Controlで実行可能なトラブルシューティング・アクティビティ。

Java Mission Controlでは次のトラブルシューティング・アクティビティを実行できます。

- Java管理コンソール(JMX)が実行中のJVMに接続し、主な特性をリアルタイムに収集して表示します。

- JVMのユーザー指定のカスタム・アクションとルールのトリガーを設定します。

- 試験的プラグイン(WLS、DTrace、JOverflowおよび他のJMCツールのプラグイン)でトラブルシューティング・アクティビティを実行します。

- DTraceプラグインはDScript言語を拡張したもので、自己記述型のイベントを生成します。Javaフライト・レコーダのように可視化できます。

- JOverflowはヒープの未使用領域(空/疎コレクション)を分析するためのもう1つのプラグイン・ツールです。JOverflowプラグインの使用を最適化するには、JDK 8以降のリリースの使用をお薦めします。

- Java Mission ControlのJavaフライト記録(JFR)はイベントの分析に使用できます。事前構成済のタブを使用すると、コード、メモリーとGC、スレッドとI/Oといったよく使用される様々な領域で簡単にドリルダウンできます。一般イベント・タブと操作イベント・タブを組み合せて使用することで、特定のプロパティのイベント・セットを詳細にドリルダウンし、手早く焦点を絞ることができます。通常、「イベント」タブにはチェック・ボックスがあり、「操作セット」内のイベントのみが表示されます。

- JFRをJMCクライアント用のプラグインとして使用すると、論理的にグループ化された表、グラフおよびダイアルに診断情報が表示されます。これを使用すると、問題に焦点を当てるために必要となる期間および詳細レベルを選択できます。「Javaフライト・レコーダ」を参照してください。

- Java Mission Controlプラグインは、Java Management Extensions (JMX)エージェントを使用してJVMに接続します。JMXは、アプリケーション、デバイス、サービス、Java仮想マシンなどのリソースを管理およびモニタリングするための標準APIです。

JMCの詳細は、JMCのドキュメントを参照してください。

Javaフライト記録とは

Java Flight Recorder (JFR)は商用機能です。開発者のデスクトップおよびラップトップ上では無償で、また評価目的であればテスト環境、開発環境および本番環境で使用できます。

ただし、JFRを本番サーバーで有効にする場合は、商用ライセンスが必要です。それ以外の目的でJMC UIをJDK上で使用する場合、商用ライセンスは必要ありません。

JFRの商用機能と入手方法の詳細は、製品マニュアルを参照してください。

JFRの商用ライセンスの詳細は、ライセンス契約を参照してください。

Javaフライト・レコーダは、JavaランタイムおよびJavaランタイムで実行されているJavaアプリケーションの詳細情報を記録します。記録プロセスによるオーバーヘッドはほとんど発生しません。データは、イベントと呼ばれるタイム・スタンプ付きのデータ・ポイントとして記録されます。一般的なイベントには、ロックを待機中のスレッド、ガベージ・コレクション、定期的なCPU使用率データなどがあります。

フライト記録を作成する場合は、保存するイベントを選択します。これは記録テンプレートと呼ばれています。一部のテンプレートは、ごく基本的なイベントのみを保存し、パフォーマンスにほとんど影響を与えません。他のテンプレートは、いくらかのパフォーマンス・オーバーヘッドが発生し、追加情報を得るためにGCをトリガーする場合もあります。通常、オーバーヘッドが数パーセントを超えることはほとんどありません。

フライト記録は、パフォーマンスの問題からメモリー・リーク、重いロック競合まで、広範囲の問題のデバッグに使用できます。

次のトピックでは、Javaフライト記録を作成する記録の種類について説明します。

- 記録の種類

記録の種類

連続記録とプロファイリング記録の2種類のフライト記録があります。

- 連続記録: 連続記録は、常時オンの記録で、たとえば、最近6時間のデータを保存します。アプリケーションで問題が発生した場合は、たとえば、最後の1時間のデータをダンプし、問題発生時に何が起こったかを調べることができます。
連続記録のデフォルト設定は、低いオーバーヘッドで記録プロファイルを使用することです。このプロファイルは、ヒープの統計や割当てプロファイリングを取得しませんが、役立つデータを大量に収集します。

連続記録は、常に実行されていることが重要であり、あまり発生しない問題をデバッグする際に便利です。記録は、jcmdまたはJMCのいずれかを使用して手動でダンプできます。また、特定の条件が満たされたときにフライト記録をダンプするためにJMCにトリガーを設定することもできます。

- プロファイリング記録: プロファイリング記録は、オンになると、一定時間実行した後、停止する記録です。通常、プロファイリング記録では、より多くのイベントが有効であり、パフォーマンスへの影響がいくらか大きくなる場合があります。オンにするイベントは、プロファイリング記録の使用に応じて変更できます。
プロファイリング記録の一般的なユース・ケースは次のとおりです。

- 最もよく実行されるメソッドおよび最も多くのオブジェクトが作成される場所のプロファイルを記録する。

- メモリー・リーク発生の指標となる、ヒープ使用量の増加を示しているクラスを探す。

- 同期に起因するボトルネックおよび多数のこのようなユース・ケースを探す。

プロファイリング記録は、特定の問題のトラブルシューティングをしていない場合にも、多くの情報を提供します。プロファイリング記録では、アプリケーションの全体像を見て、ボトルネックまたは改善の必要な領域を見つけることができます。

注意:

通常のオーバーヘッドは2%程度であるので、パフォーマンスまたはレイテンシに対して非常に慎重である場合を除き、プロファイリング記録は本番環境で実行できます(これはJFRの主要ユース・ケースの1つです)。

フライト記録の作成方法

次の項では、フライト記録を作成する3つの方法について説明します。

- Java Mission Controlによるフライト記録の作成

- コマンド行での起動フラグによるフライト記録の作成

- トリガーによる自動記録

Java Mission Controlによるフライト記録の作成

フライト記録を簡単に管理するには、Java Mission Control (JMC)を使用します。

前提条件:
開始するには、図2-1に示すように左端のフレームのJVMブラウザで自分のサーバーを見つけます。

図2-1 Java Mission Control - サーバーを見つける

デフォルトでは、ローカルで実行中のすべてのJVM一覧が表示されます。リモートJVM (JMCを実行しているユーザーと同じ有効ユーザーで実行)はリモートJMXエージェントを使用するように設定する必要があります。次に、新しいJVM接続ボタンをクリックし、ネットワークの詳細を入力します。

JDK 8u40より前のリリースでは、-XX:+UnlockCommercialFeatures -XX:FlightRecorderフラグを指定してJVMが起動されている必要がありました。

JDK 8u40リリースからは、Javaフライト・レコーダを実行時に有効にすることができます。

Java Mission Controlを使用してフライト記録を作成する3つの方法は次のとおりです。

1. 実行中の記録の確認: JVMブラウザのノードを展開して実行中の記録を表示します。図2-2は、実行中の連続記録(無限大記号を持つ)と一定時間のプロファイリング記録を示しています。

図2-2 Java Mission Control - 実行中の記録

任意の記録を右クリックして、記録をダンプ、編集または停止します。プロファイリング記録を停止しても記録ファイルは作成され、プロファイリング記録を閉じると記録が破棄されます。

2. 連続記録のダンプ: JVMブラウザで連続記録を右クリックし、ファイルへのダンプを選択します。図2-3に示すように、表示されるダイアログ・ボックスで、使用可能なすべてのデータまたは記録の最後の部分のみをダンプすることを選択します。

図2-3 Java Mission Control - 連続記録のダンプ

3. 新しい記録の開始: 新しい記録を開始するには、記録するJVMを右クリックし、「フライト記録の開始」を選択します。図2-4に示すようなウィンドウが表示されます。

図2-4 Java Mission Control - フライト記録の開始

図2-4に示すように、「一定時間の記録」(プロファイリング記録)または「連続記録」のいずれかを選択します。連続記録の場合は、保存するイベントの最大サイズまたは最大保持時間も指定します。

「イベント設定」を選択することもできます。独自のテンプレートを作成することもできますが、すべてのユース・ケースの99%では、連続テンプレート(記録オーバーヘッドが非常に低い )またはプロファイリング・テンプレート(より多くのデータと若干のオーバヘッド)のいずれかを選択できます。注意: プロファイリング記録の通常のオーバーヘッドは2%程度です。

完了後、「次へ」をクリックします。図2-5に示すように、次の画面では、様々なユース・ケースに合わせてテンプレートに修正を加えることができます。

図2-5 Java Mission Control - プロファイリングのイベント・オプション

デフォルトの設定では、データとパフォーマンスのバランスが良くとれています。いくつかのケースでは、別のイベントを追加する場合があります。たとえば、メモリー・リークを調査し、多くのJavaヒープを消費するオブジェクトを見つける場合は、ヒープ統計を有効にします。これにより、記録の開始時と終了時に2つのOldコレクションがトリガーされるので、待機時間が増えます。また、キャッチされた例外も含め、スローされたすべての例外を表示することも選択できます。一部のアプリケーションでは、多くのイベントが生成されます。

しきい値は、記録イベントの長さです。たとえば、デフォルトでは、10msを超える同期イベントが収集されます。これは、スレッドが10msを超えてロックを待機している場合、イベントが保存されることを意味します。短い競合の詳細データを取得するには、この値を下げることができます。

「スレッド・ダンプ」の設定は、定期的なスレッド・ダンプを実行するためのオプションを提供します。これらは、診断コマンドThread.printの使用、またはjstackツールの使用により取得されるような、通常のテキストのスレッド・ダンプです。スレッド・ダンプは、イベントを補完します。

コマンド行での起動フラグによるフライト記録の作成

コマンド行で起動フラグを使用して、プロファイリング記録の作成、連続記録、診断コマンドの使用を行います。

JFRフラグの詳細な説明は、Java Platform, Standard Editionツール・リファレンスの高度なランタイム・オプションに関する項を参照してください。

コマンド行で起動フラグを使用してフライト記録を作成する3つの方法は次のとおりです。

1. プロファイリング記録の開始: -XX:StartFlightRecordingオプションを使用して、アプリケーションの起動時に一定時間の記録を設定できます。JFRは商用の機能なので、-XX:+UnlockCommercialFeaturesオプションを指定する必要があります。次の例は、MyAppアプリケーションを実行し、JVMを起動してから20秒後に60秒間の記録を開始し、それをmyrecording.jfrという名前のファイルに保存する方法を示しています。

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:StartFlightRecording=delay=20s,duration=60s,name=myrecording,filename=C:\TEMP\myrecording.jfr,settings=profile MyApp

settingsパラメータにはテンプレートへのパスまたはテンプレート名を指定します。デフォルトのテンプレートはjre/lib/jfrフォルダにあります。2つの標準プロファイルがあります: デフォルトは、主に連続記録のために作成されたオーバーヘッドの低い設定で、プロファイルは、より多くのデータを収集し、主にプロファイリング記録用です。

2. 連続記録の開始: -XX:FlightRecorderOptionsを使用して、コマンド行から連続記録を開始することもできます。これらのフラグは、必要に応じて後でダンプできる連続記録を開始します。次の例は、連続記録を示しています。一時データはディスクに保存され(/tmpフォルダ)、6時間のデータが保存されます。

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,repository=/tmp,maxage=6h,settings=default MyApp

注意:
実際に記録をダンプするときは、ダンプ・ファイルのための新しい場所を指定するので、リポジトリ内のファイルは一時的です。

Javaフライト記録の構成および管理の詳細は、Java Flight Recorderランタイム・ガイドを参照してください。

3. 診断コマンドの使用:

Javaコマンド行の診断コマンドを使用して記録を制御することもできます。診断コマンドを実行するもっとも単純な方法は、Javaインストール・ディレクトリにある jcmdツールを使用することです。詳細は、「jcmdユーティリティ」を参照してください。

トリガーによる自動記録

条件が満たされた場合にフライト記録を自動的に開始またはダンプするようにJava Mission Controlを設定できます。これはJMXコンソールから実行します。JMXコンソールを起動するには、JVMブラウザでアプリケーションを見つけてそれを右クリックし、JMXブラウザの起動を選択します。

図2-6に示すように、画面の下部にある「トリガー」タブを選択します。

図2-6 Java Mission Control - 自動記録

アプリケーション内の任意のMBeanにトリガーを作成することがきます。高いCPU使用率、デッドロック・スレッド、過剰なライブ・セットなどのいくかの一般的な条件のためのデフォルト・トリガーが設定されています。「追加」を選択して、アプリケーション内の任意のMBean(アプリケーションに固有のものを含む)を選択します。トリガーを選択するとき、満たす必要のある条件も選択できます。詳細は、右上の疑問符をクリックして組込みのヘルプを参照してください。

複数のトリガーを実行するには、トリガーの横のボックスをクリックします。

条件を選択したら、「アクション」タブをクリックします。次に、条件が満たされたときに実行する処理を選択します。最後に、図2-7に示すように、連続記録のダンプまたは期間限定のフライト記録の開始のいずれかを選択します。

図2-7 Java Mission Control - トリガーの使用

フライト記録の検査

フライト記録を検査するためのサンプルJFRの取得方法、およびフライト記録を分析するためのJava Mission Controlの様々なタブについての説明。

次の項で説明します。

- 検査のためのサンプルJFRの取得方法

- 範囲ナビゲータ

- 「全般」タブ

- 「メモリー」タブ

- 「コード」タブ

- 「スレッド」タブ

- 「I/O」タブ

- 「システム」タブ

- 「イベント」タブ

検査のためのサンプルJFRの取得方法

フライト記録を作成して、Mission Controlでそれを開くことができます。

フライト記録を作成した後、Mission Controlでそれを開くことができます。フライト記録を検査するための簡単な方法:

- Mission Controlを開き、「JVMブラウザ」タブを選択する。

- Mission Controlを実行しているJVMオプションを選択して短い記録を作成する。

フライト記録を開くと、「全般」、「メモリー」、「コード」、「スレッド」、「I/O」、「システム」および「イベント」などのいくつかのメイン・タブが表示されます。プラグインがインストールされている場合は、他のメイン・タブも表示されることがあります。これらのメイン・タブには、それぞれのサブタブがあります。疑問符をクリックして組込みのヘルプのメイン・タブおよびサブタブに関する項を参照してください。

範囲ナビゲータ

範囲ナビゲータを使用して、フライト記録を検査します。

各タブの上部のビューに、範囲ナビゲータがあります。

図2-8 フライト記録の検査 - 範囲ナビゲータ

図2-8の縦棒は、記録内のイベントを表します。棒の高さが高いほど、より多くのイベントがその時点で発生しています。選択された時間の端をドラッグして、記録のズーム・インまたはズーム・アウトができます。範囲ナビゲータをダブルクリックすると、ズーム・アウトして記録全体が表示されます。すべてのサブタブに同じズーム・レベルを適用するには、「選択の同期」チェック・ボックスをクリックします。

詳細は、組込みヘルプの範囲ナビゲータの使用に関する項を参照してください。イベントの名前は、タブの名前どおりです。

「全般」タブ

「全般」タブのフライト記録を検査します。

「全般」タブには、アプリケーション全般について説明するいくつかのサブタブが含まれています。図2-9に示すように、最初のサブタブは「概要」で、ここには最大ヒープ使用量、合計CPU使用率およびGC一時休止時間などの基本情報が表示されます。

図2-9 フライト記録の検査 - 「全般」タブ

また、CPU使用率の経時的推移、アプリケーションでの使用状況とマシンでの合計を調べます。このタブは、アプリケーションで直接に問題が発生したときに調べるための便利なタブです。たとえば、100%近くまで急上昇しているCPU使用率、低すぎるCPU使用率、またはガベージ・コレクションの一時休止などに注意してください。

注意: ヒープ統計を指定して開始されたプロファイリング記録は、記録の開始時と終了時に、他のコレクションよりも長くかかる可能性がある、2つのOldコレクションを取得します。

また、「JVM情報」サブタブには、JVM情報が表示されます。起動パラメータのサブタブの「システム・プロパティ」には、設定されたすべてのシステム・プロパティが表示され、「記録」には、オンにされたイベントなど、特定の記録に関する情報が表示されます。すべてのタブおよびサブタブについて組込みの詳細を参照するには、疑問符をクリックしてください。

「メモリー」タブ

「メモリー」タブのフライト記録を検査します。

「メモリー」タブには、ガベージ・コレクション、割当てパターンおよびオブジェクト統計に関する情報が含まれています。このタブは、メモリー・リークのデバッグおよびGCのチューニングのために特に役立ちます。

「概要」タブには、メモリー使用量に関する一般情報およびガベージ・コレクションに関する統計情報が表示されます。注意: 「概要」タブのグラフでは、目盛の最大値がマシンで使用可能な物理メモリーです。そのため、Javaヒープは、下部のわずかな部分のみを取る場合があります。

「メモリー」タブの3つのサブタブは次のとおりです。

- 「ガベージ・コレクション」タブ: 「ガベージ・コレクション」タブには、メモリー使用量の経時的推移およびすべてのガベージ・コレクションに関する情報が表示されます。

図2-10 フライト記録の検査 - ガベージ・コレクション

図2-10に示すように、ヒープ使用量のスパイク状のパターンはまったく正常です。ほとんどのアプリケーションでは、一時オブジェクトが絶えず割り当てられています。条件が満たされると、ガベージ・コレクション(GC)がトリガーされ、使用されなくなったすべてのオブジェクトが削除されます。したがって、ヒープ使用量は、GCがトリガーされるまで絶え間なく増加した後、急激に減少します。

JavaのほとんどのGCには、何らかの小規模なガベージ・コレクションがあります。Old GCではJavaヒープ全体が対象となり、他のGCではヒープの一部を調べる場合があります。Oldコレクション後のヒープ使用量は、アプリケーションで使用されているメモリーであり、ライブ・セットと呼ばれています。

ヒープ統計を有効にして生成されるフライト記録は、Old GCで開始および終了します。GCの一覧からそのOld GCを選択し、「全般」タブを選択して「GC理由」を参照すると、ヒープ検査によるGCの開始であることがわかります。通常、これらのGCは、他のGCよりも少し時間がかかります。

メモリー・リークに対処するための最適な方法としては、最初と最後のOld GCでの「GC後のヒープ」の値を確認してください。この値が経時的に増加している場合は、メモリー・リークの可能性があります。

「GC回数」タブには、GCの実行にかかった時間とGCのためにアプリケーションが完全に一時休止する時間に関する情報があります。「GC構成」タブには、GCの構成に関する情報があります。これらのタブの詳細は、右上の疑問符をクリックして組込みのヘルプを参照してください。

- 「割当て」タブ: 図2-11では、行われたすべてのメモリー割当ての選択を示しています。Javaの小さなオブジェクトは、TLAB (Thread Local Area Buffer)に割り当てられます。TLABは、新しいオブジェクトが割り当てられている小さなメモリー領域です。TLABが一杯になると、スレッドが新しいものを取得します。すべてのメモリー割当てをログすると、オーバーヘッドが生じるので、新しいTLABをトリガーしたすべての割当てがログされます。大きいオブジェクトは、TLAB外に割り当てられ、これらもログされます。

図2-11 フライト記録の検査 - 「割当て」タブ

各クラスのメモリー割当てを推定するには、「新しいTLABの割当て」タブ、「割当て」タブの順に選択します。これらの割当ては、新しいTLABをトリガーするために起こるオブジェクトの割当てです。char型の配列は、新しいTLABを最も多くトリガーします。どれだけのメモリーがchar型の配列として割り当てられているかは不明です。TLABのサイズは、char型の配列によって割り当てられるメモリーを推定するのに役立ちます。

図2-11は、最も多くのメモリーを割り当てるchar型の配列の例です。いずれかのクラスをクリックして、これらの割当てのスタック・トレースを参照してください。記録の例では、すべての割当てプレッシャの44%がchar型の配列に由来し、27%がStringBuilder.toStringから呼び出されるArray.copyOfRangeに由来することを示しています。通常、StringBuilder.toStringは、Throwable.printStackTraceおよびStackTraceElement.toStringによって呼び出されます。これらのメソッドの呼出しを確認するには、さらに展開します。

注意: アプリケーションによる一時オブジェクトの割当てが多いほど、アプリケーションで必要なガベージ・コレクションが増えます。「割当て」タブは、ほとんどの割当てを見つけてアプリケーションのGCプレッジャを減らすことに役立ちます。「TLAB外の割当て」タブでは、通常、「新しいTLABの割当て」タブよりもメモリー・プレッシャが少ない、大きいメモリー割当てを参照できます。

- 「オブジェクト統計」タブ: 「オブジェクト統計」タブには、ほとんどのライブ・セットを持つクラスが表示されます。ライブ・セットについて理解するには、「「メモリー」タブ」の「ガベージ・コレクション」サブタブを参照してください。図2-12では、フライト記録のヒープ統計を示しています。データを表示するには、フライト記録のヒープ統計を有効にしてください。下部の「上位の増加」タブでは、フライト記録中に各オブジェクト・タイプのサイズがどれだけ増加したかを示しています。特定のオブジェクト・タイプのサイズが大きく増加した場合にはメモリー・リークを示していますが、わずかな変動は正常です。特に、標準Javaクラス以外の上位の増加を調査します。

図2-12 フライト記録の検査 - 「オブジェクト統計」タブ

「コード」タブ

「コード」タブのフライト記録を検査します。

「コード」タブには、アプリケーションが時間の大半を費やしている場所の情報が含まれます。「概要」サブタブには、最も多くの実行時間を費やしたパッケージおよびクラスが表示されます。これはサンプリングからのデータです。JFRでは、一定の間隔で実行中のスレッドのサンプルを取ります。実際のコードを実行しているスレッドのみがサンプリングされ、スリープ中、ロックまたはI/Oを待機中のスレッドは表示されません。

実際のコードを実行するためのアプリケーション時間の詳細は、「ホット・メソッド」サブタブを参照してください。

図2-13 フライト記録の検査 - 「コード」タブ

図2-13では、最も多くサンプリングされたメソッドを示しています。これらの呼出し元を確認するには、サンプルを展開します。HashMap.getEntryの呼出しが多い場合は、最も多く呼び出したメソッドが見つかるまでこのノードを展開します。このタブは、アプリケーションのボトルネックを見つけるのに最適です。

「呼出しツリー」サブタブでは、同じイベントが下から(Thread.runなどから)順に表示されます。

「例外」サブタブには、スローされた例外が表示されます。デフォルトではエラーのみがログされますが、すべての例外を含めるには、新しい記録を開始するときにこの設定を変更します。

「コンパイル」サブタブには、アプリケーションが実行されていたときにコンパイルされたメソッドが表示されます。

「クラスのロード」サブタブには、時間の経過とともにロードされたクラス数、実際にロードされたクラスおよびアンロードされたクラスが表示されます。このサブタブは、記録の開始時にクラス・ロード・イベントが有効であった場合にのみ情報が表示されます。

これらのタブの詳細は、右上の疑問符をクリックして組込みのヘルプを参照してください。

「スレッド」タブ

「スレッド」タブのフライト記録を検査します。

「スレッド」タブには、スレッド、ロックの競合およびその他の待機時間に関する情報が含まれます。

「概要」サブタブには、CPU使用率およびスレッド数の経時的な推移が示されます。

「ホット・スレッド」サブタブには、ほとんどのコード実行を行うスレッドが表示されます。この情報は、「コード」タブの「ホット・メソッド」サブタブと同じサンプリング・データに基づいています。

「競合」タブは、ロックの競合に起因するボトルネックを見つけるのに役立ちます。

図2-14 フライト記録の検査 - 「競合」タブ

図2-14では、同期のための待機時間が長いオブジェクトを示しています。各オブジェクトの待機時間のスタック・トレースを参照するには、クラスを選択します。通常、これらの一時休止は、別のスレッドがロックを保持している同期メソッドによって生じます。

注意:
デフォルトでは、10msより長い同期イベントのみが記録されますが、記録の開始時にこのしきい値を下げることができます。

「待機時間」サブタブは、たとえば、スリープまたは待機の呼出し、ソケットからの読取り、ファイルI/Oの待機など、その他の待機時間の原因を示します。

「スレッド・ダンプ」サブタブには、記録内でトリガーできる定期的なスレッド・ダンプが表示されます。

「ロック・インスタンス」サブタブには、同期のために最も待機しているオブジェクトの正確なインスタンスが表示されます。

これらのタブの詳細は、右上の疑問符をクリックして組込みのヘルプを参照してください。

「I/O」タブ

「I/O」タブには、ファイルの読取り、ファイルの書込み、ソケットの読取りおよびソケットの書込みに関する情報が表示されます。

このタブは、特に、I/O操作に時間がかかる場合など、アプリケーションに応じて役立ちます。

注意:
デフォルトでは、10msより長いイベントのみが表示されます。しきい値は、新しい記録を作成するときに変更できます。

「システム」タブ

「システム」タブは、アプリケーションが実行されているマシンのCPU、メモリーおよびOSに関する詳細を提供します。

また、環境変数およびJVMと同時に実行されている他のプロセスも表示されます。

「イベント」タブ

「イベント」タブには、記録のすべてのイベントが表示されます。

これは様々な方法で使用できる詳細タブです。これらのタブの詳細は、右上の疑問符をクリックして組込みのヘルプを参照してください。

jcmdユーティリティ

jcmdユーティリティを使用してJVMに診断コマンド・リクエストを送信し、JVMではこれらのリクエストを使用してJavaフライト記録の制御、トラブルシューティング、JVMおよびJavaアプリケーションの診断を行います。

jcmdはJVMが実行されているものと同じマシン上で使用され、JVMの起動で使用されたものと同じ有効なユーザーおよびグループ識別子を持っている必要があります。

特殊コマンドjcmd <process id/main class> PerfCounter.printは、プロセス内のすべてのパフォーマンス・カウンタを出力します。

コマンドjcmd <process id/main class> <command> [options]は、JVMにコマンドを送信します。

次の例に、jcmdユーティリティによるJVMへの診断コマンド・リクエストを示します。

> jcmd
5485 sun.tools.jcmd.JCmd
2125 MyProgram

> jcmd MyProgram help (or "jcmd 2125 help")
2125:
The following commands are available:
JFR.configure
JFR.stop
JFR.start
JFR.dump
JFR.check
VM.log
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.status
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
Compiler.directives_clear
Compiler.directives_remove
Compiler.directives_add
Compiler.directives_print
VM.print_touched_methods
Compiler.codecache
Compiler.codelist
Compiler.queue
VM.classloader_stats
Thread.print
JVMTI.data_dump
JVMTI.agent_load
VM.stringtable
VM.symboltable
VM.class_hierarchy
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.finalizer_info
GC.heap_info
GC.run_finalization
GC.run
VM.info
VM.uptime
VM.dynlibs
VM.set_flag
VM.flags
VM.system_properties
VM.command_line
VM.version
help
For more information about a specific command use 'help <command>'.

> jcmd MyProgram help Thread.print
2125:
Thread.print
Print all threads with stacktraces.

Impact: Medium: Depends on the number of threads.

Permission: java.lang.management.ManagementPermission(monitor)

Syntax : Thread.print [options]

Options: (options must be specified using the <key> or <key>=<value> syntax)
-l : [optional] print java.util.concurrent locks (BOOLEAN, false)

> jcmd MyProgram Thread.print
2125:
2014-07-04 15:58:56
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.0-b69 mixed mode):
...

次の項では、jcmdユーティリティでの便利なコマンドおよびトラブルシューティング手法について説明します。

- jcmdユーティリティの有用なコマンド

- jcmdコマンド・ユーティリティを使用したトラブルシューティング

jcmdユーティリティの有用なコマンド

使用可能な診断コマンドはHotSpot VMのバージョンごとに異なる場合があるので、jcmd <process id/main class> helpを使用して、使用可能なすべてのオプションを確認することをお薦めします。

jcmdツールに含まれている非常に役立つコマンドの一部を次に示します。jcmd <process id/main class> help <command>を使用すれば、該当するコマンドのその他のオプションをいつでも確認できることを覚えておいてください。

- HotSpotおよびJDKのバージョンIDの出力
jcmd <process id/main class> VM.version

- VMに設定されているすべてのシステム・プロパティの出力

数百行の情報が表示される可能性があります。

jcmd <process id/main class> VM.system_properties

- VMで使用されるすべてのフラグの出力

フラグを指定していない場合でも、いくつかのデフォルト値(初期ヒープ・サイズや最大ヒープ・サイズなど)が表示されます。

jcmd <process id/main class> VM.flags

- 稼働時間(秒)の出力

jcmd <process id/main class> VM.uptime

- クラス・ヒストグラムの作成

出力結果が冗長になる可能性があるので、出力をファイルにリダイレクトできます。内部クラスとアプリケーション固有のクラスの両方がリストに含まれます。メモリーの消費量が最も多いクラスから降順に表示されます。

jcmd <process id/main class> GC.class_histogram

- ヒープ・ダンプの作成

jcmd GC.heap_dump filename=Myheapdump

これはjmap -dump:file=<file> <pid>を使用する方法と同じですが、推奨ツールはjcmdです。

- ヒープ・ヒストグラムの作成

jcmd <process id/main class> GC.class_histogram filename=Myheaphistogram

これはjmap -histo <pid>を使用する方法と同じですが、推奨ツールはjcmdです。

- スタック・トレースのあるすべてのスレッドの出力

jcmd <process id/main class> Thread.print

jcmdコマンド・ユーティリティを使用したトラブルシューティング

jcmdユーティリティを使用してトラブルシューティングします。

jcmdユーティリティでは、次のトラブルシューティング・オプションを使用できます。

- 記録の開始

たとえば、識別子が7060の実行中Javaプロセスで2分間の記録を開始し、それを現在のディレクトリのmyrecording.jfrに保存するには、次を使用します。

jcmd 7060 JFR.start name=MyRecording settings=profile delay=20s duration=2m filename=C:\TEMP\myrecording.jfr

- 記録の確認

JFR.check診断コマンドは、実行中の記録を確認します。次に例を示します。

jcmd 7060 JFR.check

- 記録の停止

JFR.stop診断コマンドは、実行中の記録を停止し、記録データを破棄することもできます。次に例を示します。

jcmd 7060 JFR.stop

- 記録のダンプ

JFR.dump診断コマンドは、実行中の記録を停止し、記録をファイルにダンプすることもできます。次に例を示します。

jcmd 7060 JFR.dump name=MyRecording filename=C:\TEMP\myrecording.jfr

- ヒープ・ダンプの作成

ヒープ・ダンプを作成する推奨方法を次に示します。

jcmd <pid> GC.heap_dump filename=Myheapdump

- ヒープ・ヒストグラムの作成

ヒープ・ヒストグラムを作成する推奨方法を次に示します。

jcmd <pid> GC.class_histogram filename=Myheaphistogram

ネイティブ・メモリー・トラッキング

ネイティブ・メモリー・トラッキング(NMT)は、Java HotSpot VMの内部メモリー使用状況を追跡するJava HotSpot VM機能です。

NMTでは、非JVMコードによるメモリーの割当てを追跡しないので、ネイティブ・コードのメモリー・リークを検出するには、オペレーティング・システムでサポートされたツールの使用が必要になる場合があります。

次の項では、VM内部メモリー割当てのモニターおよびVMメモリー・リークの診断方法について説明します。

- NMTによるメモリー・リークの検出

- VM内部メモリーをモニターする方法

- NMTのメモリー・カテゴリ

NMTによるメモリー・リークの検出

ネイティブ・メモリー・トラッキングを使用してメモリー・リークを検知する手順。

メモリー・リークを検出するには、次の手順を実行します。

1. 次のコマンドライン・オプションを使用して、サマリーまたは詳細なトラッキングが指定されたJVMを起動します: -XX:NativeMemoryTracking=summaryまたは-XX:NativeMemoryTracking=detail。

2. 初期のベースラインを設定します。NMTベースライン機能を使用して、開発とメンテナンスのときに比較するベースラインを取得するには、次を実行します: jcmd <pid> VM.native_memory baseline。

3. 次を使用して、メモリーの変更をモニターします: jcmd <pid> VM.native_memory detail.diff

4. アプリケーションのメモリー・リークが少量の場合、それが明らかになるまで時間がかかる場合があります。

VM内部メモリーをモニターする方法

ネイティブ・メモリー・トラッキングはメモリーをモニターし、開発またはメンテナンス時にアプリケーションのメモリー使用量が増え始めないように設定できます。

NMTのメモリー・カテゴリの詳細は、表2-1を参照してください。

次の項では、NMTのサマリーまたは詳細データを取得する方法、およびサンプル出力の解釈の方法を説明します。

- サンプル出力の解釈: 次のサンプル出力から、予約済メモリーとコミット済メモリーを確認できます。コミット済メモリーのみが実際に使用されることに注意してください。たとえば、-Xms100m -Xmx1000mで実行した場合、JVMでは1000MBがJavaヒープ用に予約されます。初期のヒープ・サイズは100MBのみであるため、最初に100MBのみがコミットされます。アドレス領域がほぼ無制限である64ビット・マシンの場合、JVMで多くのメモリーが予約されても問題はありません。コミットされるメモリーが次第に増加し、スワッピングやネイティブ・メモリー不足(OOM)状態になる可能性がある場合に、問題が発生します。

アリーナは、mallocを使用して割り当てられるメモリー・チャンクです。スコープを終了またはコード領域を離れるとき、これらのチャンクからメモリーがバルクで解放されます。これらのチャンクは、一時メモリーを保持するために他のサブシステムで再利用できます(プリスレッドの割当てなど)。アリーナのmallocポリシーでは、メモリー・リークがないことが確認されます。したがって、個々のオブジェクトではなく、Arena全体が追跡されます。一部の初期メモリーは追跡できません。

NMTを有効にすると、JVMパフォーマンスが5-10%低下し、NMTのメモリー使用量により、すべてのmallocメモリーに2マシン・ワードがmallocヘッダーとして追加されます。NMTのメモリー使用量も、NMTにより追跡されます。

Total:  reserved=664192KB,  committed=253120KB                                           <--- total memory tracked by Native Memory Tracking

-                 Java Heap (reserved=516096KB, committed=204800KB)                      <--- Java Heap
(mmap: reserved=516096KB, committed=204800KB)

-                     Class (reserved=6568KB, committed=4140KB)                          <--- class metadata
(classes #665)                                               <--- number of loaded classes
(malloc=424KB, #1000)                                        <--- malloc'd memory, #number of malloc
(mmap: reserved=6144KB, committed=3716KB)

-                    Thread (reserved=6868KB, committed=6868KB)
(thread #15)                                                 <--- number of threads
(stack: reserved=6780KB, committed=6780KB)                   <--- memory used by thread stacks
(malloc=27KB, #66)
(arena=61KB, #30)                                            <--- resource and handle areas

-                      Code (reserved=102414KB, committed=6314KB)
(malloc=2574KB, #74316)
(mmap: reserved=99840KB, committed=3740KB)

-                        GC (reserved=26154KB, committed=24938KB)
(malloc=486KB, #110)
(mmap: reserved=25668KB, committed=24452KB)

-                  Compiler (reserved=106KB, committed=106KB)
(malloc=7KB, #90)
(arena=99KB, #3)

-                  Internal (reserved=586KB, committed=554KB)
(malloc=554KB, #1677)
(mmap: reserved=32KB, committed=0KB)

-                    Symbol (reserved=906KB, committed=906KB)
(malloc=514KB, #2736)
(arena=392KB, #1)

-           Memory Tracking (reserved=3184KB, committed=3184KB)
(malloc=3184KB, #300)

-        Pooled Free Chunks (reserved=1276KB, committed=1276KB)
(malloc=1276KB)

-                   Unknown (reserved=33KB, committed=33KB)
(arena=33KB, #1)

- 詳細データの取得: ネイティブ・メモリー使用量の詳細ビューを取得するには、次のコマンドライン・オプションを使用してJVMを起動します: -XX:NativeMemoryTracking=detailこれは、最も多くのメモリーを割り当てるメソッドを正確に追跡します。NMTを有効にすると、JVMパフォーマンスが5-10%低下し、NMTのメモリー使用量により、すべてのmallocメモリーに2ワードがmallocヘッダーとして追加されます。NMTのメモリー使用量も、NMTにより追跡されます。

次の例は、追跡レベルが詳細に設定された仮想メモリーのサンプル出力を示しています。このサンプル出力を得るための1つの方法は、jcmd <pid> VM.native_memory detailを実行することです。

Virtual memory map:

[0x8f1c1000 - 0x8f467000] reserved 2712KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0x8f1c1000 - 0x8f467000] committed 2712KB from [Thread::record_stack_base_and_size()+0xca]

[0x8f585000 - 0x8f729000] reserved 1680KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0x8f585000 - 0x8f729000] committed 1680KB from [Thread::record_stack_base_and_size()+0xca]

[0x8f930000 - 0x90100000] reserved 8000KB for GC
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0x8f930000 - 0x90100000] committed 8000KB from [PSVirtualSpace::expand_by(unsigned int)+0x95]

[0x902dd000 - 0x9127d000] reserved 16000KB for GC
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0x902dd000 - 0x9127d000] committed 16000KB from [os::pd_commit_memory(char*, unsigned int, unsigned int, bool)+0x36]

[0x9127d000 - 0x91400000] reserved 1548KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0x9127d000 - 0x91400000] committed 1548KB from [Thread::record_stack_base_and_size()+0xca]

[0x91400000 - 0xb0c00000] reserved 516096KB for Java Heap                                                                            <--- reserved memory range
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x190]                  <--- callsite that reserves the memory
[0x91400000 - 0x93400000] committed 32768KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x3e8]               <--- committed memory range and its callsite
[0xa6400000 - 0xb0c00000] committed 172032KB from [PSVirtualSpace::expand_by(unsigned int)+0x95]                             <--- committed memory range and its callsite

[0xb0c61000 - 0xb0ce2000] reserved 516KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0xb0c61000 - 0xb0ce2000] committed 516KB from [Thread::record_stack_base_and_size()+0xca]

[0xb0ce2000 - 0xb0e83000] reserved 1668KB for GC
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0xb0ce2000 - 0xb0cf0000] committed 56KB from [PSVirtualSpace::expand_by(unsigned int)+0x95]
[0xb0d88000 - 0xb0d96000] committed 56KB from [CardTableModRefBS::resize_covered_region(MemRegion)+0xebf]
[0xb0e2e000 - 0xb0e83000] committed 340KB from [CardTableModRefBS::resize_covered_region(MemRegion)+0xebf]

[0xb0e83000 - 0xb7003000] reserved 99840KB for Code
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0xb0e83000 - 0xb0e92000] committed 60KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x3e8]
[0xb1003000 - 0xb139b000] committed 3680KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x37a]

[0xb7003000 - 0xb7603000] reserved 6144KB for Class
from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555]
[0xb7003000 - 0xb73a4000] committed 3716KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x37a]

[0xb7603000 - 0xb760b000] reserved 32KB for Internal
from [PerfMemory::create_memory_region(unsigned int)+0x8ba]

[0xb770b000 - 0xb775c000] reserved 324KB for Thread Stack
from [Thread::record_stack_base_and_size()+0xca]
[0xb770b000 - 0xb775c000] committed 324KB from [Thread::record_stack_base_and_size()+0xca]

- NMTベースラインからの差分の取得: サマリーおよび詳細レベルの追跡では、アプリケーションが起動され実行された後、ベースラインを設定できます。これを行うには、アプリケーションのウォーム・アップ後にjcmd <pid> VM.native_memory baselineを実行します。次に、jcmd <pid> VM.native_memory summary.diffまたはjcmd <pid> VM.native_memory detail.diffを実行できます。

次の例は、ベースラインの設定以降に使用されたネイティブ・メモリーの差分サマリーのサンプル出力を示し、これはメモリー・リークを見つけるのに優れた方法です。

Total:  reserved=664624KB  -20610KB, committed=254344KB -20610KB                         <--- total memory changes vs. earlier baseline. '+'=increase '-'=decrease

-                 Java Heap (reserved=516096KB, committed=204800KB)
(mmap: reserved=516096KB, committed=204800KB)

-                     Class (reserved=6578KB +3KB, committed=4530KB +3KB)
(classes #668 +3)                                            <--- 3 more classes loaded
(malloc=434KB +3KB, #930 -7)                                 <--- malloc'd memory increased by 3KB, but number of malloc count decreased by 7
(mmap: reserved=6144KB, committed=4096KB)

-                    Thread (reserved=60KB -1129KB, committed=60KB -1129KB)
(thread #16 +1)                                              <--- one more thread
(stack: reserved=7104KB +324KB, committed=7104KB +324KB)
(malloc=29KB +2KB, #70 +4)
(arena=31KB -1131KB, #32 +2)                                 <--- 2 more arenas (one more resource area and one more handle area)

-                      Code (reserved=102328KB +133KB, committed=6640KB +133KB)
(malloc=2488KB +133KB, #72694 +4287)
(mmap: reserved=99840KB, committed=4152KB)

-                        GC (reserved=26154KB, committed=24938KB)
(malloc=486KB, #110)
(mmap: reserved=25668KB, committed=24452KB)

-                  Compiler (reserved=106KB, committed=106KB)
(malloc=7KB, #93)
(arena=99KB, #3)

-                  Internal (reserved=590KB +35KB, committed=558KB +35KB)
(malloc=558KB +35KB, #1699 +20)
(mmap: reserved=32KB, committed=0KB)

-                    Symbol (reserved=911KB +5KB, committed=911KB +5KB)
(malloc=519KB +5KB, #2921 +180)
(arena=392KB, #1)

-           Memory Tracking (reserved=2073KB -887KB, committed=2073KB -887KB)
(malloc=2073KB -887KB, #84 -210)

-        Pooled Free Chunks (reserved=2624KB -15876KB, committed=2624KB -15876KB)
(malloc=2624KB -15876KB)

次の例は、ベースライン以降に使用されたネイティブ・メモリーの差分詳細を示すサンプル出力で、これはメモリー・リークを見つけるのに優れた方法です。

Details:

[0x01195652] ChunkPool::allocate(unsigned int)+0xe2
(malloc=482KB -481KB, #8 -8)

[0x01195652] ChunkPool::allocate(unsigned int)+0xe2
(malloc=2786KB -19742KB, #134 -618)

[0x013bd432] CodeBlob::set_oop_maps(OopMapSet*)+0xa2
(malloc=591KB +6KB, #681 +37)

[0x013c12b1] CodeBuffer::block_comment(int, char const*)+0x21                <--- [callsite address] method name + offset
(malloc=562KB +33KB, #35940 +2125)               <--- malloc'd amount, increased by 33KB #malloc count, increased by 2125

[0x0145f172] ConstantPool::ConstantPool(Array<unsigned char>*)+0x62
(malloc=69KB +2KB, #610 +15)

...

[0x01aa3ee2] Thread::allocate(unsigned int, bool, unsigned short)+0x122
(malloc=21KB +2KB, #13 +1)

[0x01aa73ca] Thread::record_stack_base_and_size()+0xca
(mmap: reserved=7104KB +324KB, committed=7104KB +324KB)

JConsole

JDKダウンロードに含まれるもう1つの便利なツールは、JConsoleモニタリング・ツールです。このツールはJMXに準拠しています。このツールは、JVMの組込みJMXインストゥルメンテーションを使用して、実行中のアプリケーションのパフォーマンスやリソース消費に関する情報を提供します。

このツールは、JDKダウンロードに含まれてはいますが、JREで配備されたアプリケーションのモニターや管理にも使用できます。

JConsoleツールは、スレッド使用量やメモリー消費量などの有用な情報、およびクラス・ロード、実行時コンパイル、オペレーティング・システムに関する詳細情報を表示する目的で、任意のJavaアプリケーションに接続できます。

この出力は、メモリー・リーク、過剰なクラス・ロードや実行中のスレッドなどの問題の高レベルの診断に役立ちます。これは、チューニングやヒープ・サイズ決定にも役立つ可能性があります。

JConsoleを使用すれば、モニタリングだけでなく、実行中のシステム内のいくつかのパラメータを動的に変更することもできます。たとえば、-verbose:gcオプションの設定を変更できるので、実行中のアプリケーションに対するガベージ・コレクションのトレース出力を動的に有効または無効にできます。

次の項では、JConsoleツールによるトラブルシューティング手法について説明します。

- JConsoleツールによるトラブルシューティング

- JConsoleによるローカルおよびリモート・アプリケーションのモニター

JConsoleツールによるトラブルシューティング

JConsoleを使用して、データをモニターします。

次のリストは、JConsoleツールを使用してモニターできるデータの概要を示しています。各見出しはツール内のタブ・ペインに対応しています。

- 概要
このペインには、ヒープ・メモリー使用量、スレッド数、クラス数、およびCPU使用率の経時的推移を示すグラフが表示されます。この概要では、いくつかのリソースのアクティビティを一度に視覚化できます。

- メモリー

- 選択された1つのメモリー領域(ヒープ、非ヒープ、各種メモリー・プール)について

- メモリー使用量の経時的推移を示すグラフ

- 現在のメモリー・サイズ

- コミットされたメモリーの量

- 最大メモリー・サイズ

- ガベージ・コレクタの情報(実行されたコレクションの回数や、ガベージ・コレクションの実行に費やされた合計時間など)

- 現在使用中のヒープおよび非ヒープ・メモリーのパーセントを示すグラフ

さらにこのペインでは、ガベージ・コレクションの実行を要求できます。

- スレッド

- スレッド使用量の経時的推移を示すグラフ。

- ライブ・スレッド: 現在のライブ・スレッド数。

- ピーク: JVM起動後のライブ・スレッドの最大数。

- 選択された1つのスレッドの名前、状態、およびスタック・トレース、およびブロックされたスレッドの場合は、スレッドが獲得を待機しているシンクロナイザとロックを所有しているスレッド。

- 「デッドロックの検出」ボタンは、デッドロック検出を実行する要求をターゲット・アプリケーションに送信し、各デッドロック・サイクルをそれぞれ異なるタブに表示します。

- クラス

- ロード済クラス数の経時的推移を示すグラフ

- 現在メモリーにロードされているクラスの数

- JVMの起動後にメモリーにロードされたクラスの合計数(その後アンロードされたものも含む)

- JVMの起動後にメモリーからアンロードされたクラスの合計数

- VMサマリー

- 一般的な情報(JConsole接続データ、JVMの稼働時間、JVMで消費されたCPU時間、コンパイラ名、合計コンパイル時間など)。

- スレッドおよびクラスのサマリー情報

- メモリーおよびガベージ・コレクションの情報(ファイナライズ保留中のオブジェクトの数など)

- オペレーティング・システムに関する情報(物理特性、実行中のプロセス用の仮想メモリー量、スワップ領域など)

- JVM自体に関する情報(引数やクラス・パスなど)

- MBeans
このペインには、接続済のJMXエージェントに登録されたすべてのプラットフォームおよびアプリケーションMBeanを示すツリー構造が表示されます。ツリー内でMBeanを選択すると、その属性、操作、通知、およびその他の情報が表示されます。

- 操作(存在する場合)を呼び出せます。たとえば、com.sun.managementドメインに含まれるHotSpotDiagnostic MBeanの操作dumpHeapは、ヒープ・ダンプを実行します。この操作の入力パラメータは、ターゲットVMが実行されているマシン上でのヒープ・ダンプ・ファイルのパス名になります。

- 書込み可能な属性値を設定できます。たとえば、特定のVMフラグの値を設定、設定解除、または変更するには、HotSpotDiagnostic MBeanのsetVMOption操作を呼び出します。フラグは、DiagnosticOptions属性の値のリストによって示されます。

- 通知(存在する場合)をサブスクライブするには、「サブスクライブ」および「サブスクライブ解除」ボタンを使用します。

JConsoleによるローカルおよびリモート・アプリケーションのモニター

JConsoleは、ローカル・アプリケーションとリモート・アプリケーションの両方をモニターできます。接続先JMXエージェントを指定する引数付きでツールを起動した場合、ツールは指定されたアプリケーションのモニタリングを自動的に開始します。

ローカル・アプリケーションをモニターするには、コマンドjconsolepid (pidはアプリケーションのプロセスID)を実行します。

リモート・アプリケーションをモニターするには、コマンドjconsolehostname: portnumber (hostnameはアプリケーションを実行しているホストの名前、portnumberはJMXエージェントの有効化時に指定したポート番号)を実行します。

jconsoleコマンドを引数なしで実行した場合、このツールはまず、モニター対象のローカルまたはリモート・プロセスを指定するための「新規接続」ウィンドウを表示します。「接続」メニューを使えば別のホストにいつでも接続できます。

最新のJDKリリースでは、モニター対象アプリケーションを起動する際にオプションは不要です。

モニタリング・ツールの出力例として、図2-15はヒープ・メモリー使用量のグラフを示しています。

図2-15 JConsoleからのサンプル出力

jdbユーティリティ

jdbユーティリティは、コマンド行デバッガの例としてJDKに含まれています。jdbユーティリティは、Java Debug Interface (JDI)を使ってターゲットJVMを起動または接続します。

$JAVA_HOME/demo/jpda/examples.jarにはjdbのソース・コードが含まれています。

JDIは、(通常はリモートの)仮想マシンの実行状態にアクセスする必要があるデバッガや同様のシステムに役立つ情報を提供する、高レベルのJava APIです。JDIはJava Platform Debugger Architecture (JPDA)のコンポーネントです。「Java Platform Debugger Architecture」を参照してください。

次の項では、jdbユーティリティのトラブルシューティング手法を提供します。

- jdbユーティリティによるトラブルシューティング

- プロセスの接続

- 同一マシン上のコア・ファイルへの接続

- 別のマシンからのコア・ファイルまたはハング・プロセスへの接続

jdbユーティリティによるトラブルシューティング

jdbユーティリティはリモート・デバッグに使用するデバッガ・コネクタをモニターするために使用します。

JDIでは、コネクタを使用してデバッガをターゲットJVMに接続します。JDKには従来、ターゲットJVMとのデバッグ・セッションを開始して確立するコネクタと、(TCP/IPまたは共有メモリー・トランスポートを使用した)リモート・デバッグに使用されるコネクタが付属しています。

JDKにはさらに、Java言語デバッガからクラッシュ・ダンプやハングアップ・プロセスに接続することを許可する、Serviceability Agent (SA)コネクタもいくつか付属しています。これは、クラッシュまたはハング・アップの際にアプリケーションが何をしていたのかを確認するのに役立つ可能性があります。

これらのコネクタはSACoreAttachingConnector、SADebugServerAttachingConnector、およびSAPIDAttachingConnectorです。

これらのコネクタは通常、NetBeans統合開発環境(IDE)や商用IDEなどのエンタープライズ・デバッガで使用されます。次の項では、jdbコマンド行デバッガでこれらのコネクタを使用する方法を示します。

コマンドjdb -listconnectorsは、使用可能なコネクタの一覧を出力します。コマンドjdb -helpは、コマンドの使用方法のヘルプを出力します。

Java Platform, Standard Editionツール・リファレンスのjdbユーティリティに関する項を参照してください

プロセスの接続

次の例では、SA PID接続コネクタを使用してプロセスに接続します。ターゲット・プロセスは特別なオプションを使用して起動されるわけではありません。つまり、-agentlib:jdwpオプションは必要ありません。このコネクタがプロセスに接続するときは、読取り専用モードで行われます。つまり、デバッガはスレッドや実行中のアプリケーションを調べることはできますが、何も変更できません。デバッガが接続されている間、プロセスは休止しています。

次の例のコマンドは、sun.jvm.hotspot.jdi.SAPIDAttachingConnectorという名前のコネクタを使用するようにjdbに指示しています。これはクラス名ではなくコネクタ名です。コネクタはpidという引数を1つ取り、その値はターゲット・プロセスのプロセスID (9302)です。

$ jdb -connect sun.jvm.hotspot.jdi.SAPIDAttachingConnector:pid=9302

Initializing jdb ...
> threads
Group system:
(java.lang.ref.Reference$ReferenceHandler)0xa Reference Handler unknown
(java.lang.ref.Finalizer$FinalizerThread)0x9  Finalizer         unknown
(java.lang.Thread)0x8                         Signal Dispatcher running
(java.lang.Thread)0x7                         Java2D Disposer   unknown
(java.lang.Thread)0x2                         TimerQueue        unknown
Group main:
(java.lang.Thread)0x6                         AWT-XAWT          running
(java.lang.Thread)0x5                         AWT-Shutdown      unknown
(java.awt.EventDispatchThread)0x4             AWT-EventQueue-0  unknown
(java.lang.Thread)0x3                         DestroyJavaVM     running
(sun.awt.image.ImageFetcher)0x1               Image Animator 0  sleeping
(java.lang.Thread)0x0                         Intro             running
> thread 0x7
Java2D Disposer[1] where
[1] java.lang.Object.wait (native method)
[2] java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:116)
[3] java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:132)
[4] sun.java2d.Disposer.run (Disposer.java:125)
[5] java.lang.Thread.run (Thread.java:619)
Java2D Disposer[1] up 1
Java2D Disposer[2] where
[2] java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:116)
[3] java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:132)
[4] sun.java2d.Disposer.run (Disposer.java:125)
[5] java.lang.Thread.run (Thread.java:619)

この例では、threadsコマンドを使用してすべてのスレッドの一覧を取得しています。次に、thread 0x7コマンドで特定のスレッドを選択し、whereコマンドを使用してスレッド・ダンプを取得しています。次に、up 1コマンドを使用してスタック内で1フレーム上に移動し、whereコマンドを再度使用してスレッド・ダンプを取得しています。

同一マシン上のコア・ファイルへの接続

SAコア接続コネクタは、デバッガをコア・ファイルに接続するために使用されます。

クラッシュの後にコア・ファイルが作成されている場合があります。「システム・クラッシュのトラブルシューティング」を参照してください。コア・ファイルは、gcoreコマンド(Oracle Solarisオペレーティング・システムの場合)、またはgdbのgcoreコマンド(Linuxの場合)を使用して取得することもできます。コア・ファイルはそのコア・ファイルが作成された時点のプロセスのスナップショットであるため、コネクタは読取り専用モードで接続します。つまり、デバッガはクラッシュ発生時のスレッドや実行中のアプリケーションを調べることができます。

次の例のコマンドは、sun.jvm.hotspot.jdi.SACoreAttachingConnectorという名前のコネクタを使用するようにjdbに指示しています。このコネクタはjavaExecutable、coreという2つの引数を取ります。javaExecutable引数はJavaバイナリの名前を示します。core引数は、コア・ファイル名です(次の例に示すように、PID 20441を持つプロセスからのコア)。

$ jdb -connect sun.jvm.hotspot.jdi.SACoreAttachingConnector:javaExecutable=$JAVA_HOME/bin/java,core=core.20441

別のマシンからのコア・ファイルまたはハング・プロセスへの接続

デバッガがインストールされたマシン上で、SAデバッグ・サーバー接続コネクタを使用してデバッグ・サーバーに接続できます。

別のマシンからトランスポートされたコア・ファイルをデバッグするには、オペレーティング・システムのバージョンとライブラリが一致する必要があります。この場合はまず、SAデバッグ・サーバーという名前のプロキシ・サーバーを実行できます。その後、デバッガがインストールされたマシン上で、SAデバッグ・サーバー接続コネクタを使ってデバッグ・サーバーに接続できます。

たとえば、マシン1、マシン2という2つのマシンがあるとします。マシン1でコア・ファイルが使用可能、マシン2でデバッガが使用可能です。次の例に示すように、マシン1でSAデバッグ・サーバーが起動されます。

$ jsadebugd $JAVA_HOME/bin/java core.20441

jsadebugdコマンドは2つの引数を取ります。最初の引数は実行可能ファイルの名前です。通常、これはjavaですが、別の名前のこともあります(埋込みVMの場合など)。2番目の引数は、コア・ファイルの名前です。この例でのコア・ファイルは、gcoreユーティリティを使用してPID 20441のプロセスに対して取得されました。

次の例に示すように、マシン2では、デバッガはSAデバッグ・サーバー接続コネクタを使用してリモートSAデバッグ・サーバーに接続します。

$ jdb -connect sun.jvm.hotspot.jdi.SADebugServerAttachingConnector:debugServerName=machine1

この例のコマンドは、sun.jvm.hotspot.jdi.SADebugServerAttachingConnectorという名前のコネクタを使用するようにjdbに指示しています。このコネクタは、debugServerNameという引数(SAデバッグ・サーバーが実行されているマシンのホスト名またはIPアドレス)を1つ持ちます。

注意:

SAデバッグ・サーバーはハングアップ・プロセスのリモート・デバッグにも使用できます。その場合、単一の引数(プロセスのPID)を取ります。さらに、同じマシン上で複数のデバッグ・サーバーを実行する必要がある場合は、それぞれに一意のIDを指定する必要があります。SAデバッグ・サーバー接続コネクタで、このIDは追加のコネクタ引数として提供されます。

jinfoユーティリティ

jinfoコマンド行ユーティリティは、実行中のJavaプロセスまたはクラッシュ・ダンプから構成情報を取得し、JVMの起動時に使用されたシステム・プロパティまたはコマンド行フラグを出力します。

JVMおよびJavaアプリケーションに関する問題を診断するには、Java Mission Control、Java Flight Recorderおよびjcmdユーティリティを使用できます。診断機能を強化し、パフォーマンスのオーバーヘッドを削減するには、以前のjinfoユーティリティのかわりに最新のユーティリティjcmdを使用してください。

jinfoユーティリティでは、jsadebugdデーモンを使用してリモート・マシン上のプロセスやコア・ファイルを照会することもできます。

注意:
この場合、出力に時間がかかります。

jinfoユーティリティの-flagオプションを使用すれば、指定されたJavaプロセスの特定のJVMフラグの値を動的に設定、設定解除、または変更できます。「Java HotSpot VMコマンド行オプション」を参照してください。

次の例は、PID番号が29620のJavaプロセスのjinfoユーティリティの出力を示しています。

$ jinfo 29620
Attaching to process ID 29620, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 1.6.0-rc-b100
Java System Properties:

java.runtime.name = Java(TM) SE Runtime Environment
sun.boot.library.path = /usr/jdk/instances/jdk1.6.0/jre/lib/sparc
java.vm.version = 1.6.0-rc-b100
java.vm.vendor = Sun Microsystems Inc.
java.vendor.url = http://java.sun.com/
path.separator = :
java.vm.name = Java HotSpot(TM) Client VM
file.encoding.pkg = sun.io
sun.java.launcher = SUN_STANDARD
sun.os.patch.level = unknown
java.vm.specification.name = Java Virtual Machine Specification
user.dir = /home/js159705
java.runtime.version = 1.6.0-rc-b100
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
java.endorsed.dirs = /usr/jdk/instances/jdk1.6.0/jre/lib/endorsed
os.arch = sparc
java.io.tmpdir = /var/tmp/
line.separator =

java.vm.specification.vendor = Sun Microsystems Inc.
os.name = SunOS
sun.jnu.encoding = ISO646-US
java.library.path = /usr/jdk/instances/jdk1.6.0/jre/lib/sparc/client:/usr/jdk/instances/jdk1.6.0/jre/lib/sparc:
/usr/jdk/instances/jdk1.6.0/jre/../lib/sparc:/net/gtee.sfbay/usr/sge/sge6/lib/sol-sparc64:
/usr/jdk/packages/lib/sparc:/lib:/usr/lib
java.specification.name = Java Platform API Specification
java.class.version = 50.0
sun.management.compiler = HotSpot Client Compiler
os.version = 5.10
user.home = /home/js159705
user.timezone = US/Pacific
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = ISO646-US
java.specification.version = 1.6
java.class.path = /usr/jdk/jdk1.6.0/demo/jfc/Java2D/Java2Demo.jar
user.name = js159705
java.vm.specification.version = 1.0
java.home = /usr/jdk/instances/jdk1.6.0/jre
sun.arch.data.model = 32
user.language = en
java.specification.vendor = Sun Microsystems Inc.
java.vm.info = mixed mode, sharing
java.version = 1.6.0-rc
java.ext.dirs = /usr/jdk/instances/jdk1.6.0/jre/lib/ext:/usr/jdk/packages/lib/ext
sun.boot.class.path = /usr/jdk/instances/jdk1.6.0/jre/lib/resources.jar:
/usr/jdk/instances/jdk1.6.0/jre/lib/rt.jar:/usr/jdk/instances/jdk1.6.0/jre/lib/sunrsasign.jar:
/usr/jdk/instances/jdk1.6.0/jre/lib/jsse.jar:
/usr/jdk/instances/jdk1.6.0/jre/lib/jce.jar:/usr/jdk/instances/jdk1.6.0/jre/lib/charsets.jar:
/usr/jdk/instances/jdk1.6.0/jre/classes
java.vendor = Sun Microsystems Inc.
file.separator = /
java.vendor.url.bug = http://java.sun.com/cgi-bin/bugreport.cgi
sun.io.unicode.encoding = UnicodeBig
sun.cpu.endian = big
sun.cpu.isalist =

VM Flags:

次のトピックでは、jinfoユーティリティによるトラブルシューティング手法について説明します。

- jinfoユーティリティによるトラブルシューティング

jinfoユーティリティによるトラブルシューティング

jinfoの出力には、java.class.pathおよびsun.boot.class.pathの設定が表示されます。

ターゲットJVMを-classpathおよび-Xbootclasspath引数付きで起動した場合、jinfoの出力にはjava.class.pathとsun.boot.class.pathの設定が含まれます。この情報は、クラス・ローダーの問題を調査する際に必要となる可能性があります。

jinfoツールは、プロセスから情報を取得するだけでなく、コア・ファイルを入力として使用できます。たとえば、Oracle Solarisオペレーティング・システムの場合、前述の例ではgcoreユーティリティを使用してプロセスのコア・ファイルを取得できます。コア・ファイルの名前はcore.29620となり、プロセスの作業ディレクトリ内に生成されます。次の例に示すように、Java実行可能ファイルとコア・ファイルへのパスを、jinfoユーティリティの引数として指定する必要があります。

$ jinfo $JAVA_HOME/bin/java core.29620

バイナリ名はjavaでないことがあります。これは、JNI呼び出しAPIを使用してVMが作成されたときに発生します。jinfoツールでは、コア・ファイルの生成元のバイナリが必要です。

jmapユーティリティ

jmapコマンド行ユーティリティは、実行中のVMまたはコア・ファイルのメモリー関係の統計を出力します。

このユーティリティは、jsadebugdデーモンを使ってリモート・マシン上のプロセスやコア・ファイルを照会することもできます。この場合、出力に時間がかかります。

JVMおよびJavaアプリケーションに関する問題を診断するには、Java Mission Control、Java Flight Recorderおよびjcmdユーティリティを使用できます。診断機能を強化し、パフォーマンスのオーバーヘッドを削減するには、以前のjmapユーティリティのかわりに最新のユーティリティjcmdの使用をお薦めします。

プロセスまたはコア・ファイルでコマンド行オプションなしでjmapを使用された場合、ロードされた共有オブジェクトの一覧が出力されます(出力はOracle Solarisオペレーティング・システムのpmapユーティリティに似ています)。より詳細な情報を得るために、オプション-heap、-histo、または-permstatを使用できます。これらのオプションについては、以降の各サブセクションで説明します。

さらに、JDK 7リリースで導入された-dump:format=b,file=filenameオプションを使用すると、jmapによって、Javaヒープが指定されたファイルにバイナリ形式でダンプされます。

ハングアップ・プロセスが原因でjmappidコマンドが反応しない場合は、-Fオプションを使用して(Oracle SolarisおよびLinuxオペレーティング・システムのみ) Serviceability Agentの使用を強制できます。

次の項では、jmapコマンドの使用方法およびトラブルシューティング手法について説明し、実行中のVMまたはコア・ファイルのメモリー関連の統計情報を出力する例を示します。

- ヒープの構成と使用量

- ヒープ・ヒストグラム

- Permanent世代の統計

ヒープの構成と使用量

jmap -heapコマンドを使用してJavaヒープ情報を取得します。

次のJavaヒープ情報を取得するには、-heapオプションを使用します。

- ガベージ・コレクション(GC)アルゴリズムに固有の情報。GCアルゴリズムの名前(パラレルGCなど)やアルゴリズム固有の詳細(パラレルGCのスレッド数など)が含まれます。

- コマンド行オプションとして指定されたか、またはマシンの構成に基づいてVMによって選択された可能性のあるヒープ構成。

- ヒープ使用量のサマリー: このツールは、各世代(ヒープ領域)について、合計ヒープ容量、使用中のメモリー、および使用可能な空きメモリーを出力します。領域の集まりとして構成された世代(New世代など)では、領域固有のメモリー・サイズ・サマリーが含まれます。

次の例は、jmap -heapコマンドの出力を示しています。

$ jmap -heap 29620
Attaching to process ID 29620, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 1.6.0-rc-b100

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize      = 67108864 (64.0MB)
NewSize          = 2228224 (2.125MB)
MaxNewSize       = 4294901760 (4095.9375MB)
OldSize          = 4194304 (4.0MB)
NewRatio         = 8
SurvivorRatio    = 8
PermSize         = 12582912 (12.0MB)
MaxPermSize      = 67108864 (64.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 2031616 (1.9375MB)
used     = 70984 (0.06769561767578125MB)
free     = 1960632 (1.8698043823242188MB)
3.4939673639112905% used
Eden Space:
capacity = 1835008 (1.75MB)
used     = 36152 (0.03447723388671875MB)
free     = 1798856 (1.7155227661132812MB)
1.9701276506696428% used
From Space:
capacity = 196608 (0.1875MB)
used     = 34832 (0.0332183837890625MB)
free     = 161776 (0.1542816162109375MB)
17.716471354166668% used
To Space:
capacity = 196608 (0.1875MB)
used     = 0 (0.0MB)
free     = 196608 (0.1875MB)
0.0% used
tenured generation:
capacity = 15966208 (15.2265625MB)
used     = 9577760 (9.134063720703125MB)
free     = 6388448 (6.092498779296875MB)
59.98769400974859% used
Perm Generation:
capacity = 12582912 (12.0MB)
used     = 1469408 (1.401336669921875MB)
free     = 11113504 (10.598663330078125MB)
11.677805582682291% used

ヒープ・ヒストグラム

-histoオプションを指定してjmapコマンドを使用すると、クラス固有のヒープ・ヒストグラムを取得できます。

指定されたパラメータに応じて、jmap -histoコマンドは実行中のプロセスまたはコア・ファイルのヒープ・ヒストグラムを出力できます。

実行中のプロセスに対してコマンドが実行されると、ツールはクラスごとにオブジェクト数、メモリー・サイズ(バイト)、および完全修飾クラス名を出力します。Java HotSpot VMの内部クラスは山カッコで囲まれます。ヒストグラムは、ヒープがどのように使用されているかを理解するのに役立ちます。オブジェクトのサイズを得るには、合計サイズをそのオブジェクト型の数で割る必要があります。

次の例は、29620というPID番号を持つプロセスに対してjmap -histoコマンドを実行した場合の出力を示しています。

$ jmap -histo 29620
num   #instances    #bytes  class name
--------------------------------------
1:      1414     6013016  [I
2:       793      482888  [B
3:      2502      334928  <constMethodKlass>
4:       280      274976  <instanceKlassKlass>
5:       324      227152  [D
6:      2502      200896  <methodKlass>
7:      2094      187496  [C
8:       280      172248  <constantPoolKlass>
9:      3767      139000  [Ljava.lang.Object;
10:       260      122416  <constantPoolCacheKlass>
11:      3304      112864  <symbolKlass>
12:       160       72960  java2d.Tools$3
13:       192       61440  <objArrayKlassKlass>
14:       219       55640  [F
15:      2114       50736  java.lang.String
16:      2079       49896  java.util.HashMap$Entry
17:       528       48344  [S
18:      1940       46560  java.util.Hashtable$Entry
19:       481       46176  java.lang.Class
20:        92       43424  javax.swing.plaf.metal.MetalScrollButton
... more lines removed here to reduce output...
1118:         1           8  java.util.Hashtable$EmptyIterator
1119:         1           8  sun.java2d.pipe.SolidTextRenderer
Total    61297    10152040

コア・ファイルに対してjmap -histoコマンドが実行された場合、ツールはクラスごとにサイズ、カウント、クラス名を出力します。Java HotSpot VMの内部クラスには、先頭にアスタリスク(*)が付けられます。

jmap -histoコマンドをコア・ファイルで実行した場合の出力を示します。

& jmap -histo /net/koori.sfbay/onestop/jdk/6.0/promoted/all/b100/binaries/solaris-sparcv9/bin/java core
Attaching to core core from executable /net/koori.sfbay/onestop/jdk/6.0/
promoted/all/b100/binaries/solaris-sparcv9/bin/java, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 1.6.0-rc-b100
Iterating over heap. This may take a while...
Heap traversal took 8.902 seconds.

Object Histogram:

Size    Count    Class description
-------------------------------------------------------
4151816    2941    int[]
2997816    26403    * ConstMethodKlass
2118728    26403    * MethodKlass
1613184    39750    * SymbolKlass
1268896    2011    * ConstantPoolKlass
1097040    2011    * InstanceKlassKlass
882048    1906    * ConstantPoolCacheKlass
758424    7572    char[]
733776    2518    byte[]
252240    3260    short[]
214944    2239    java.lang.Class
177448    3341    * System ObjArray
176832    7368    java.lang.String
137792    3756    java.lang.Object[]
121744    74    long[]
72960    160    java2d.Tools$3
63680    199    * ObjArrayKlassKlass
53264    158    float[]
... more lines removed here to reduce output...

Permanent世代の統計

Permanent世代は、クラスやメソッド・オブジェクトなどの仮想マシン自体を反映したデータをすべて保持するヒープ領域です。

この領域は、Java仮想マシン仕様ではメソッド領域とも呼ばれます。

非常に多くのクラスを動的に生成してロードするアプリケーション(Java Server PagesやWebコンテナなど)では、Permanent世代のサイズの構成が重要になる可能性があります。アプリケーションでロードされたクラスが多すぎた場合、そのアプリケーションは次のエラーで終了する可能性があります。

Exception in thread thread_name java.lang.OutOfMemoryError: PermGen space

「OutOfMemoryError例外の理解」を参照してください。

Permanent世代に関する詳細情報を得るには、jmapコマンドの-permstatオプションを使ってPermanent世代内のオブジェクトの統計を出力できます。

次の例は、29620というPID番号を持つプロセスに対して実行されたjmap -permstatコマンドの出力を示しています。

$ jmap -permstat 29620
Attaching to process ID 29620, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 1.6.0-rc-b100
12674 intern Strings occupying 1082616 bytes.
finding class loader instances ..Unknown oop at 0xd0400900
Oop's klass is 0xd0bf8408
Unknown oop at 0xd0401100
Oop's klass is null
done.
computing per loader stat ..done.
please wait.. computing liveness.........................................done.
class_loader    classes bytes   parent_loader   alive?  type

<bootstrap>     1846 5321080  null        live   <internal>
0xd0bf3828  0      0      null         live    sun/misc/Launcher$ExtClassLoader@0xd8c98c78
0xd0d2f370  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0c99280  1   1440      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0b71d90  0      0   0xd0b5b9c0    live java/util/ResourceBundle$RBClassLoader@0xd8d042e8
0xd0d2f4c0  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0b5bf98  1    920   0xd0b5bf38      dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0c99248  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0d2f488  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0b5bf38  6   11832  0xd0b5b9c0      dead    sun/reflect/misc/MethodUtil@0xd8e8e560
0xd0d2f338  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0d2f418  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0d2f3a8  1    904     null          dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0b5b9c0  317 1397448 0xd0bf3828     live    sun/misc/Launcher$AppClassLoader@0xd8cb83d8
0xd0d2f300  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0d2f3e0  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0ec3968  1   1440      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0e0a248  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0c99210  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0d2f450  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0d2f4f8  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50
0xd0e0a280  1    904      null         dead    sun/reflect/DelegatingClassLoader@0xd8c22f50

total = 22      2186    6746816   N/A   alive=4, dead=18       N/A

クラス・ローダー・オブジェクトごとに次の詳細が出力されます。

- クラス・ローダー・オブジェクトのアドレス(ユーティリティが実行されたスナップショット時点)

- ロードされたクラスの数

- このクラス・ローダーによってロードされたすべてのクラスのメタデータで消費されているバイト数(概算)

- 親クラス・ローダー(存在する場合)のアドレス

- ローダー・オブジェクトが将来ガベージ・コレクトされるかどうかを示すliveまたはdeadマーク

- このクラス・ローダーのクラス名

jpsユーティリティ

jpsユーティリティは、ターゲット・システム上の現在のユーザーで計測されたすべてのJava HotSpot VMを一覧表示します。

VMが埋め込まれた環境、つまりjava起動ツールではなくJNI呼出しAPIを使用してVMが起動される環境では、このユーティリティが非常に役立ちます。こうした環境では、プロセス・リスト内でJavaプロセスを認識するのが常に容易であるとはかぎりません。

次の例は、jpsユーティリティの使用方法を示しています。

$ jps
16217 MyApplication
16342 jps

jpsユーティリティは、ユーザーがアクセス権を持つ仮想マシンを一覧表示します。この判断には、オペレーティング・システムに固有のアクセス制御メカニズムが使用されます。たとえば、Oracle Solarisオペレーティング・システム上のroot以外のユーザーがjpsユーティリティは、ユーザーがアクセス権を持つ仮想マシンを一覧表示します。ユーティリティを実行した場合、出力はそのユーザーのUIDで起動された仮想マシンの一覧になります。

このユーティリティには、PIDの一覧表示に加え、アプリケーションのmainメソッドに渡された引数、VM引数の完全なリスト、アプリケーションのmainクラスの完全なパッケージ名を出力するオプションも用意されています。また、リモート・システム上でjstatdデーモンが実行されていれば、jpsユーティリティはリモート・システム上のプロセスを一覧表示することもできます。

システム上でいくつかのJava Web Startアプリケーションを実行している場合、次の例に示すように、それらは同じに見えてしまいがちです。

$ jps
1271 jps
1269 Main
1190 Main

この場合、次の例に示すようにjps -mを使用して区別します。

$ jps -m
1271 jps -m
1269 Main http://bugster.central.sun.com/bugster.jnlp
1190 Main http://webbugs.sfbay/IncidentManager/incident.jnlp

jstackユーティリティ

JVMおよびJavaアプリケーションの問題を診断するには、jstackユーティリティではなくjcmdユーティリティを使用してください。

JVMおよびJavaアプリケーションに関する問題を診断するには、Java Mission Control、Java Flight Recorderおよびjcmdユーティリティを使用できます。診断機能を強化し、パフォーマンスのオーバーヘッドを削減するには、以前のjstack,ユーティリティのかわりに最新のユーティリティjcmdの使用をお薦めします。

次の項では、jstackユーティリティによるトラブルシューティング手法について説明します。

- jstackユーティリティによるトラブルシューティング

- コア・ダンプからのスタック・トレース

- 混合スタック

jstackユーティリティによるトラブルシューティング

jstackコマンド行ユーティリティは、指定されたプロセスまたはコア・ファイルに接続し、仮想マシンに接続されたすべてのスレッド(JavaスレッドやVM内部スレッドも含む)のスタック・トレースを出力する以外に、オプションでネイティブ・スタック・フレームも出力します。このユーティリティではデッドロック検出も実行されます。

このユーティリティは、jsadebugdデーモンを使ってリモート・マシン上のプロセスやコア・ファイルを照会することもできます。この場合、出力に時間がかかります。

すべてのスレッドのスタック・トレースは、デッドロックやハングアップなど、いくつかの問題の診断に役立つ可能性があります。

-lオプションは、所有可能なシンクロナイザをヒープ内で探してjava.util.concurrent.locksに関する情報を出力するように、ユーティリティに指示します。このオプションがない場合、スレッド・ダンプにはモニターの情報のみが含まれます。

jstack pidオプションの出力は、アプリケーション・コンソール(標準入力)で[Ctrl]+[\]を押すか、プロセスにquitシグナルを送信した場合に得られるものと同じです。出力例については、「Ctrl+Breakハンドラ」を参照してください。

スレッド・ダンプは、プログラム内でThread.getAllStackTracesメソッドを使って、またはデバッガ内ですべてのスレッド・スタックを出力するデバッガ・オプション(jdbサンプル・デバッガの場合はwhereコマンド)を使って取得することもできます。

コア・ダンプからのスタック・トレース

コア・ダンプからスタック・トレースを取得するにはjstackコマンドを使用します。

コア・ダンプからスタック・トレースを取得するには、次の例のように、コア・ファイルでjstackコマンドを実行します。

$ jstack $JAVA_HOME/bin/java core

混合スタック

jstackユーティリティは、混合スタックの出力に使用することもできます(つまり、Javaスタックの他にネイティブ・スタック・フレームも出力できます)。ネイティブ・フレームとは、VMコードやJNI/ネイティブ・コードに関連付けられたC/C++フレームのことです。

混合スタックを出力するには、次の例に示すように-mオプションを使用します。

$ jstack -m 21177
Attaching to process ID 21177, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 1.6.0-rc-b100
Deadlock Detection:

Found one Java-level deadlock:
=============================

"Thread1":
waiting to lock Monitor@0x0005c750 (Object@0xd4405938, a java/lang/String),
which is held by "Thread2"
"Thread2":
waiting to lock Monitor@0x0005c6e8 (Object@0xd4405900, a java/lang/String),
which is held by "Thread1"

Found a total of 1 deadlock.

----------------- t@1 -----------------
0xff2c0fbc    __lwp_wait + 0x4
0xff2bc9bc    _thrp_join + 0x34
0xff2bcb28    thr_join + 0x10
0x00018a04    ContinueInNewThread + 0x30
0x00012480    main + 0xeb0
0x000111a0    _start + 0x108
----------------- t@2 -----------------
0xff2c1070    ___lwp_cond_wait + 0x4
0xfec03638    bool Monitor::wait(bool,long) + 0x420
0xfec9e2c8    bool Threads::destroy_vm() + 0xa4
0xfe93ad5c    jni_DestroyJavaVM + 0x1bc
0x00013ac0    JavaMain + 0x1600
0xff2bfd9c    _lwp_start
----------------- t@3 -----------------
0xff2c1070    ___lwp_cond_wait + 0x4
0xff2ac104    _lwp_cond_timedwait + 0x1c
0xfec034f4    bool Monitor::wait(bool,long) + 0x2dc
0xfece60bc    void VMThread::loop() + 0x1b8
0xfe8b66a4    void VMThread::run() + 0x98
0xfec139f4    java_start + 0x118
0xff2bfd9c    _lwp_start
----------------- t@4 -----------------
0xff2c1070    ___lwp_cond_wait + 0x4
0xfec195e8    void os::PlatformEvent::park() + 0xf0
0xfec88464    void ObjectMonitor::wait(long long,bool,Thread*) + 0x548
0xfe8cb974    void ObjectSynchronizer::wait(Handle,long long,Thread*) + 0x148
0xfe8cb508    JVM_MonitorWait + 0x29c
0xfc40e548    * java.lang.Object.wait(long) bci:0 (Interpreted frame)
0xfc40e4f4    * java.lang.Object.wait(long) bci:0 (Interpreted frame)
0xfc405a10    * java.lang.Object.wait() bci:2 line:485 (Interpreted frame)
... more lines removed here to reduce output...
----------------- t@12 -----------------
0xff2bfe3c    __lwp_park + 0x10
0xfe9925e4    AttachOperation*AttachListener::dequeue() + 0x148
0xfe99115c    void attach_listener_thread_entry(JavaThread*,Thread*) + 0x1fc
0xfec99ad8    void JavaThread::thread_main_inner() + 0x48
0xfec139f4    java_start + 0x118
0xff2bfd9c    _lwp_start
----------------- t@13 -----------------
0xff2c1500    _door_return + 0xc
----------------- t@14 -----------------
0xff2c1500    _door_return + 0xc

先頭にアスタリスク(*)が付いているフレームがJavaフレーム、先頭にアスタリスクが付いていないフレームがネイティブC/C++フレームです。

このユーティリティの出力をc++filtによってパイプで連結すると、C++分解シンボル名を分解解除できます。Java HotSpot VMはC++言語で開発されているため、jstackユーティリティはJava HotSpotの内部関数のC++分解シンボル名を出力します。

c++filtユーティリティは、SUNWspro (Oracle Solarisオペレーティング・システムの場合)やgnu (Linuxの場合)というネイティブC++コンパイラ・スイートとともに配布されます。

jstatユーティリティ

jstatユーティリティは、Java HotSpot VMの組込みインストゥルメンテーションを使用して、実行中アプリケーションのパフォーマンスやリソース消費に関する情報を提供します。

このツールは、パフォーマンスの問題、特にヒープ・サイズ決定やガベージ・コレクションに関係する問題を診断するときに使用できます。jstatユーティリティでは、特殊なオプション付きでVMを起動する必要はありません。Java HotSpot VMの組込みインストゥルメンテーションはデフォルトで有効になります。このユーティリティは、Oracleがサポートしているすべてのオペレーティング・システム・プラットフォームのJDKダウンロードに含まれています。

注意:

FAT32ファイル・システム上では、インストゥルメンテーションにアクセスできません。

Java Platform, Standard Editionツール・リファレンスのjstatに関する項を参照してください。

jstatユーティリティは、仮想マシン識別子(VMID)を使用してターゲット・プロセスを識別します。ドキュメントにはVMIDの構文が記載されていますが、それに必要な唯一のコンポーネントはローカル仮想マシン識別子(LVMID)です。LVMIDは、(必ずではありませんが、)一般的にはターゲットJVMプロセスに対するオペレーティング・システムのPIDです。

jstatユーティリティで提供されるデータは、Oracle SolarisおよびLinuxオペレーティング・システムのvmstatおよびiostatで提供されるデータに似ています。

データをグラフィカルに表現する場合は、visualgcツールを使用できます。「visualgcツール」を参照してください。

-gcutilオプションの使用例を次に示します。この例では、jstatユーティリティがLVMID番号2834に接続し、250ミリ秒間隔で7つのサンプルを取っています。

$ jstat -gcutil 2834 250 7
S0     S1     E      O      M     YGC     YGCT    FGC    FGCT     GCT
0.00  99.74  13.49   7.86  95.82      3    0.124     0    0.000    0.124
0.00  99.74  13.49   7.86  95.82      3    0.124     0    0.000    0.124
0.00  99.74  13.49   7.86  95.82      3    0.124     0    0.000    0.124
0.00  99.74  13.49   7.86  95.82      3    0.124     0    0.000    0.124
0.00  99.74  13.49   7.86  95.82      3    0.124     0    0.000    0.124
0.00  99.74  13.49   7.86  95.82      3    0.124     0    0.000    0.124
0.00  99.74  13.49   7.86  95.82      3    0.124     0    0.000    0.124

この例の出力は、若い世代のコレクションが3番目と4番目のサンプル間で行われたことを示しています。コレクションには0.017秒かかっており、オブジェクトがeden領域(E)からOld領域(O)に昇格したため、old領域の使用率は46.56%から54.60%に増加しています。

-gcnewオプションの使用例を次に示します。この例では、jstatユーティリティがLVMID番号2834に接続し、250ミリ秒間隔でサンプルを取り、出力を表示しています。さらに、-h3オプションを使用して、データが3行表示されるごとに列ヘッダーを表示します。

$ jstat -gcnew -h3 2834 250
S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
192.0  192.0    0.0    0.0 15  15   96.0   1984.0    942.0    218    1.999
192.0  192.0    0.0    0.0 15  15   96.0   1984.0   1024.8    218    1.999
192.0  192.0    0.0    0.0 15  15   96.0   1984.0   1068.1    218    1.999
S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
192.0  192.0    0.0    0.0 15  15   96.0   1984.0   1109.0    218    1.999
192.0  192.0    0.0  103.2  1  15   96.0   1984.0      0.0    219    2.019
192.0  192.0    0.0  103.2  1  15   96.0   1984.0     71.6    219    2.019
S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
192.0  192.0    0.0  103.2  1  15   96.0   1984.0     73.7    219    2.019
192.0  192.0    0.0  103.2  1  15   96.0   1984.0     78.0    219    2.019
192.0  192.0    0.0  103.2  1  15   96.0   1984.0    116.1    219    2.019

この例では、ヘッダー文字列の繰返しが見られるほかにも、4番目と5番目のサンプル間で若い世代のコレクションが発生し、その継続時間が0.02秒だったことがわかります。このコレクションでは、Survivor領域1の使用率(S1U)が適切なSurvivorサイズ(DSS)を超過するのに十分なライブ・データが検出されました。この結果、オブジェクトは、Old世代(この出力には非表示)へ昇格され、殿堂入りしきい値(TT)が、15から1へ降格されました。

-gcoldcapacityオプションの使用例を次に示します。この例では、jstatユーティリティがLVMID番号21891に接続し、250ミリ秒間隔で3つのサンプルを取っています。-tオプションを使用して、最初の列にサンプルごとのタイムスタンプを表示しています。

$ jstat -gcoldcapacity -t 21891 250 3
Timestamp    OGCMN     OGCMX       OGC        OC   YGC   FGC     FGCT     GCT
150.1   1408.0   60544.0   11696.0   11696.0   194    80    2.874   3.799
150.4   1408.0   60544.0   13820.0   13820.0   194    81    2.938   3.863
150.7   1408.0   60544.0   13820.0   13820.0   194    81    2.938   3.863

Timestamp列には、ターゲットJVMの起動時からの経過時間が、秒単位で報告されています。さらに、-gcoldcapacity出力では、割当て要求または昇格要求あるいはその両方を満たすためにヒープが拡張するたびに、Old世代の容量(OGC)とOld領域の容量(OC)とが増加していることがわかります。OGCは、81番目の全世代の容量(FGC)後に、11696 KBから13820 KBへ増加しています。この世代(および領域)の最大容量は、60544 Kバイト(OGCMX)なので、まだ拡張できる余裕が残されています。

visualgcツール

visualgcツールは、ガベージ・コレクション(GC)システムのグラフィカル表示を提供します。

visualgcツールは、jstatツールに関連があります。「jstatユーティリティ」を参照してください。visualgcツールは、ガベージ・コレクション(GC)システムのグラフィカル表示を提供します。これはjstatと同様に、Java HotSpot VMの組込みインストゥルメンテーションを使用します。

visualgcツールはJDKリリースには含まれていませんが、jvmstatテクノロジ・ページから個別のダウンロードとして入手できます。

図2-16は、GCおよびヒープの視覚化を示しています。

図2-16 visualgcからのサンプル出力

Ctrl+Breakハンドラ

Oracle Solaris、Linux、Windowsなどのオペレーティング・システムのアプリケーション・コンソールで[Ctrl]キーとバックスラッシュ([\])キーを押したときの結果。

Oracle SolarisまたはLinuxオペレーティング・システムの場合、アプリケーション・コンソール(標準入力)で[Ctrl]キーとバックスラッシュ([\])キーの組合せを押すと、Java HotSpot VMからアプリケーションの標準出力にスレッド・ダンプが出力されます。Windowsの場合、同等のキー・シーケンスは[Ctrl]キーと[Break]キーです。これらのキーの組合せを表す一般的な用語は、Ctrl+Breakハンドラです。

Oracle SolarisおよびLinuxオペレーティング・システムでは、Javaプロセスがquitシグナルを受信するとスレッド・ダンプが出力されます。したがって、kill -QUIT pidコマンドを実行すると、pidをIDに持つプロセスから標準出力にスレッド・ダンプが出力されます。

次の項では、Ctrl+Breakハンドラによってトレースされるデータについて説明します。

- スレッド・ダンプ

- デッドロックの検出

- ヒープ・サマリー

スレッド・ダンプ

スレッド・ダンプは、仮想マシン内のすべてのJavaスレッドのスレッド・スタック(スレッドの状態など)で構成されています。

スレッド・ダンプによってアプリケーションが終了することはなく、スレッド情報が出力された後もアプリケーションは続行します。

次の例はスレッド・ダンプを示しています。

Full thread dump Java HotSpot(TM) Client VM (1.6.0-rc-b100 mixed mode):

"DestroyJavaVM" prio=10 tid=0x00030400 nid=0x2 waiting on condition [0x00000000..0xfe77fbf0]
java.lang.Thread.State: RUNNABLE

"Thread2" prio=10 tid=0x000d7c00 nid=0xb waiting for monitor entry [0xf36ff000..0xf36ff8c0]
java.lang.Thread.State: BLOCKED (on object monitor)
at Deadlock$DeadlockMakerThread.run(Deadlock.java:32)
- waiting to lock <0xf819a938> (a java.lang.String)
- locked <0xf819a970> (a java.lang.String)

"Thread1" prio=10 tid=0x000d6c00 nid=0xa waiting for monitor entry [0xf37ff000..0xf37ffbc0]
java.lang.Thread.State: BLOCKED (on object monitor)
at Deadlock$DeadlockMakerThread.run(Deadlock.java:32)
- waiting to lock <0xf819a970> (a java.lang.String)
- locked <0xf819a938> (a java.lang.String)

"Low Memory Detector" daemon prio=10 tid=0x000c7800 nid=0x8 runnable [0x00000000..0x00000000]
java.lang.Thread.State: RUNNABLE

"CompilerThread0" daemon prio=10 tid=0x000c5400 nid=0x7 waiting on condition [0x00000000..0x00000000]
java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" daemon prio=10 tid=0x000c4400 nid=0x6 waiting on condition [0x00000000..0x00000000]
java.lang.Thread.State: RUNNABLE

"Finalizer" daemon prio=10 tid=0x000b2800 nid=0x5 in Object.wait() [0xf3f7f000..0xf3f7f9c0]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0xf4000b40> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116)
- locked <0xf4000b40> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)

"Reference Handler" daemon prio=10 tid=0x000ae000 nid=0x4 in Object.wait() [0xfe57f000..0xfe57f940]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0xf4000a40> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:485)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)
- locked <0xf4000a40> (a java.lang.ref.Reference$Lock)

"VM Thread" prio=10 tid=0x000ab000 nid=0x3 runnable

"VM Periodic Task Thread" prio=10 tid=0x000c8c00 nid=0x9 waiting on condition

この出力は、空白行で区切られたいくつかのスレッド・エントリで構成されます。Javaスレッド(Java言語コードを実行できるスレッド)がまず出力され、それらの後にVM内部スレッドに関する情報が続きます。各スレッド・エントリは、ヘッダー行とその後に続くスレッド・スタック・トレースで構成されます。

ヘッダー行には、スレッドに関する次の情報が含まれます。

- スレッド名

- スレッドがデーモン・スレッドかどうかのマーク

- スレッド優先度(prio)

- スレッドID (tid)。メモリー内のスレッド構造体のアドレスです

- ネイティブ・スレッドのID (nid)

- スレッド・ダンプ時にスレッドが実行していた内容を示す、スレッド状態詳細は、表2-6を参照してください。

- アドレス範囲(スレッドのおおよその有効なスタック領域を表す)

デッドロックの検出

Ctrl+Breakハンドラは、スレッドのデッドロック検出にも使用できます。

Ctrl+Breakハンドラは、スレッド・スタックだけでなく、デッドロック検出アルゴリズムも実行します。デッドロックが検出された場合、次の例に示すように、Ctrl+Breakハンドラは、デッドロックされた各スレッドに関するスレッド・ダンプの後に追加情報を出力します。

Found one Java-level deadlock:
=============================
"Thread2":
waiting to lock monitor 0x000af330 (object 0xf819a938, a java.lang.String),
which is held by "Thread1"
"Thread1":
waiting to lock monitor 0x000af398 (object 0xf819a970, a java.lang.String),
which is held by "Thread2"

Java stack information for the threads listed above:
===================================================
"Thread2":
at Deadlock$DeadlockMakerThread.run(Deadlock.java:32)
- waiting to lock <0xf819a938> (a java.lang.String)
- locked <0xf819a970> (a java.lang.String)
"Thread1":
at Deadlock$DeadlockMakerThread.run(Deadlock.java:32)
- waiting to lock <0xf819a970> (a java.lang.String)
- locked <0xf819a938> (a java.lang.String)

Found 1 deadlock.

JVMフラグ-XX:+PrintConcurrentLocksが設定されている場合、Ctrl+Breakハンドラは各スレッドが所有している並行ロックのリストも出力します。

ヒープ・サマリー

Ctrl+Breakハンドラはヒープ・サマリーの出力にも使用できます。

次の例では、様々な世代(ヒープ領域)がそのサイズ、使用量、アドレス範囲とともに示されています。アドレス範囲は特に、pmapなどのツールでプロセスを調査する場合にも役立ちます。

Heap
def new generation   total 1152K, used 435K [0x22960000, 0x22a90000, 0x22e40000
)
eden space 1088K,  40% used [0x22960000, 0x229ccd40, 0x22a70000)
from space 64K,   0% used [0x22a70000, 0x22a70000, 0x22a80000)
to   space 64K,   0% used [0x22a80000, 0x22a80000, 0x22a90000)
tenured generation   total 13728K, used 6971K [0x22e40000, 0x23ba8000, 0x269600
00)
the space 13728K,  50% used [0x22e40000, 0x2350ecb0, 0x2350ee00, 0x23ba8000)
compacting perm gen  total 12288K, used 1417K [0x26960000, 0x27560000, 0x2a9600
00)
the space 12288K,  11% used [0x26960000, 0x26ac24f8, 0x26ac2600, 0x27560000)
ro space 8192K,  62% used [0x2a960000, 0x2ae5ba98, 0x2ae5bc00, 0x2b160000)
rw space 12288K,  52% used [0x2b160000, 0x2b79e410, 0x2b79e600, 0x2bd60000)

JVMフラグ-XX:+PrintClassHistogramが設定されている場合、Ctrl+Breakハンドラはヒープ・ヒストグラムを生成します。

オペレーティング・システムのネイティブ・ツール

Windows、LinuxおよびOracle Solarisオペレーティング・システムで使用可能なネイティブ・ツールのうち、トラブルシューティングやモニタリングの目的で役立つもののリスト。

各ツールについて簡単な説明が提供されます。詳細は、オペレーティング・システムのマニュアル(またはOracle SolarisおよびLinuxオペレーティング・システムのマニュアル・ページ)を参照してください。

コマンド行ユーティリティからのログ・ファイルや出力の形式は、リリースごとに異なります。たとえば、致命的エラー・ログの形式に依存するスクリプトを開発した場合、そのログ・ファイルの形式が将来のリリースで変更されれば、同じスクリプトでも動作しなくなる可能性があります。

Windows固有のデバッグ・サポートは、MSDN Developer Networkで検索することもできます。

次の項では、いくつかのオペレーティング・システム・ツールのトラブルシューティング手法および改善について説明します。

- オペレーティング・システムに基づくトラブルシューティング・ツール

- DTraceツール

- Java HotSpot VMのプローブ・プロバイダ

- pmapユーティリティの改善

- pstackユーティリティの改善

DTraceツール

Oracle Solaris 10オペレーティング・システムに含まれるDTraceツールを使用すれば、オペレーティング・システムのカーネルおよびユーザー・レベルのプログラムの動的トレースを行えます。

このツールでは、システム・コールの入口と出口、ユーザー・モード関数の入口と出口、およびその他多くのプローブ・ポイントでのスクリプトがサポートされます。スクリプトは、安全なポインタ・セマンティクスを備えた、Cに似た言語であるDプログラミング言語で記述されます。これらのスクリプトは、問題のトラブルシューティングやパフォーマンス問題の解決に役立つ可能性があります。

dtraceコマンドは、DTraceツールに対する汎用のフロント・エンドです。このコマンドには、D言語を呼び出したり、バッファ内のトレース・データを取得したり、トレース・データを書式設定/出力するための一連の基本ルーチンにアクセスしたりするための、単純なインタフェースが用意されています。

カスタマイズされた独自のDTraceスクリプトをD言語で記述したり、すでに様々なサイト上で入手可能な多くのスクリプトから1つ以上をダウンロードして使用したりすることもできます。

これらのプローブは、プロバイダと呼ばれるカーネル・モジュールによって配布され計測されます。プローブ・プロバイダによって提供されるトレースのタイプには、ユーザー命令トレース、関数境界トレース、カーネル・ロック計測、プロファイル割込み、システム・コール・トレースなどがあり、その他にもたくさんあります。独自のスクリプトを記述する場合は、D言語を使用してそれらのプローブを有効にします。この言語では、条件付きトレースや出力のフォーマットも可能です。

dtrace -lコマンドを使用すると、使用しているOracle Solarisオペレーティング・システムで使用可能な一連のプロバイダやプローブを調べることができます。

DTraceToolkitは、Oracle Solaris DTraceのオープン・コミュニティで開発されたドキュメント付きの有用なスクリプトを集めたものです。DTraceToolkitに関する項を参照してください。

Solaris動的トレース・ガイドを参照してください。

Java HotSpot VMのプローブ・プロバイダ

Java HotSpot VMにはhotspotおよびhotspot_jniという2つの組込みプローブ・プロバイダが含まれるようになりました。

これらのプロバイダは、VMの内部状態やアクティビティだけでなく、実行中のJavaアプリケーションもモニターするために使用できるプローブを配布します。

JVMプローブ・プロバイダは次のように分類できます。

- VMのライフ・サイクル: VMの初期化の開始と終了、およびVMのシャットダウン

- スレッドのライフ・サイクル: スレッドの開始と停止、スレッド名、スレッドIDなど

- クラスのロード: Javaクラスのロードとアンロード

- ガベージ・コレクション: ガベージ・コレクションの開始と停止、システム全体またはメモリー・プール別

- メソッドのコンパイル: メソッドのコンパイルの開始と終了、メソッドのロードとアンロード

- モニター・プローブ: 待機イベント、通知イベント、競合するモニターへの出入り

- アプリケーションの追跡: メソッドに入る、メソッドから戻る、Javaオブジェクトの割当て

ネイティブ・コードからJavaコードへの呼出しの場合、ネイティブ・コードはJNIインタフェース経由で呼出しを行う必要があります。hotspot_jniプロバイダは、Javaコードを呼び出したりVMの状態を検査したりするためにJNIインタフェースに用意されているメソッドごとに、入口と出口におけるDTraceプローブを管理します。

プローブ・ポイントで現在のスレッドのスタック・トレースを出力するには、ustack組込み関数を使用できます。この関数は、C/C++ネイティブ関数の名前だけでなく、Javaメソッドの名前も出力します。次の例は、あるスレッドがreadシステム・コールを呼び出したときに必ず完全なスタック・トレースを出力する単純なDスクリプトです。

#!/usr/sbin/dtrace -s
syscall::read:entry
/pid == $1 && tid == 1/ {
ustack(50, 0x2000);
}

前の例のスクリプトはread.dというファイルに格納され、次の例に示すように、トレース対象のJavaプロセスのPIDを指定することで実行されます。

read.d pid

Javaアプリケーションで多数のI/Oが生成されたり、なんらかの予期しない遅延が発生したりした場合、DTraceツールやそのustack()アクションを使用すれば、問題を診断しやすくなる可能性があります。

pmapユーティリティの改善

Oracle Solaris 10オペレーティング・システムのpmapユーティリティの改善。

pmapユーティリティは、Oracle Solaris 10オペレーティング・システムでスタック・セグメントがテキスト[stack]付きで出力されるように改善されました。このテキストはスタックの場所を見つけるのに役立ちます。

次の例は、改善されたpmapユーティリティによるスタック・トレースを示しています。

19846:    /net/myserver/export1/user/j2sdk6/bin/java -Djava.endorsed.d
00010000      72K r-x--  /export/disk09/jdk/6/rc/b63/binaries/solsparc/bin/java
00030000      16K rwx--  /export/disk09/jdk/6/rc/b63/binaries/solsparc/bin/java
00034000   32544K rwx--    [ heap ]
D1378000      32K rwx-R    [ stack tid=44 ]
D1478000      32K rwx-R    [ stack tid=43 ]
D1578000      32K rwx-R    [ stack tid=42 ]
D1678000      32K rwx-R    [ stack tid=41 ]
D1778000      32K rwx-R    [ stack tid=40 ]
D1878000      32K rwx-R    [ stack tid=39 ]
D1974000      48K rwx-R    [ stack tid=38 ]
D1A78000      32K rwx-R    [ stack tid=37 ]
D1B78000      32K rwx-R    [ stack tid=36 ]
[.. more lines removed here to reduce output ..]
FF370000       8K r-x--  /usr/lib/libsched.so.1
FF380000       8K r-x--  /platform/sun4u-us3/lib/libc_psr.so.1
FF390000      16K r-x--  /lib/libthread.so.1
FF3A4000       8K rwx--  /lib/libthread.so.1
FF3B0000       8K r-x--  /lib/libdl.so.1
FF3C0000     168K r-x--  /lib/ld.so.1
FF3F8000       8K rwx--  /lib/ld.so.1
FF3FA000       8K rwx--  /lib/ld.so.1
FFB80000      24K -----    [ anon ]
FFBF0000      64K rwx--    [ stack ]
total    167224K

pstackユーティリティの改善

Oracle Solaris 10オペレーティング・システムのpstackユーティリティの改善。

Oracle Solaris 10オペレーティング・システムより前は、pstackユーティリティはJavaをサポートしていませんでした。解釈済みおよびコンパイル済みJavaメソッドの両方で、16進アドレスを出力していました。

Oracle Solaris 10オペレーティング・システム以降のpstackコマンド行ツールは、コア・ファイルまたはライブ・プロセスから混合モードのスタック・トレース(JavaおよびC/C++フレーム)を出力します。このユーティリティは、解釈済、コンパイル済、およびインライン化されたJavaメソッドに対し、Javaメソッド名を出力します。

カスタム診断ツール

JDKには、JREに配備されているアプリケーションの問題を観察、モニター、プロファイル、デバッグ、および診断するカスタム・ツールを開発するための広範なAPIが含まれています。

新しいツールの開発はこのドキュメントの対象外です。かわりにこのセクションでは、使用可能なAPIの概要を簡単に説明します。

この項に示されたすべてのパッケージは、Java SE API仕様で説明されています。

JDKダウンロードに含まれるコード例やデモ・コードを参照してください。

次の項では、トラブルシューティングのためのカスタム診断ツールとして使用できるパッケージ、インタフェース・クラスおよびJavaデバッガについて説明します。

- java.lang.managementパッケージ

- java.lang.instrumentパッケージ

- java.lang.Threadクラス

- JVM Tool Interface

- Java Platform Debugger Architecture

Java Platform Debugger Architecture

Java Platform Debugger Architecture (JPDA)は、デバッガやデバッガに似たツールが使用するために設計されたアーキテクチャです。

JPDAは2つのプログラミング・インタフェースと1つのワイヤー・プロトコルから構成されています。

- Java Virtual Machine Tool Interface (JVM TI)は、仮想マシンへのインタフェースです。「JVM Tool Interface」を参照してください。

- Java Debug Interface (JDI)は、ユーザー・コード・レベルでの情報および要求を定義します。これは、Javaプログラミング言語アプリケーションをデバッグするためのPure Javaプログラミング言語インタフェースです。JPDAでは、JDIは、デバッガのプロセスから見たデバッグ対象プロセスの仮想マシンのリモート・ビューです。これはフロント・エンドによって実装されます。デバッガに似たアプリケーション(IDE、デバッガ、トレーサ、モニタリング・ツールなど)はクライアントとなります。

- Java Debug Wire Protocol (JDWP)は、デバッグされるプロセスと、JDIを実装したデバッガ・フロント・エンドとの間で転送される、情報および要求の形式を定義します。

jdbユーティリティは、コマンド行デバッガの例としてJDKに含まれています。jdbユーティリティは、JDIを使ってターゲットVMを起動または接続します。「jdbユーティリティ」を参照してください。

JDIを使用すれば、従来のデバッガ・タイプのツールだけでなく、ポストモーテム診断の際や、ツールが非協力的なプロセス(ハングアップ・プロセスなど)に接続する必要があるシナリオで役に立つツールを開発することもできます。

NMTのメモリー・カテゴリ

NMTで使用されるネイティブ・メモリー・トラッキングのメモリー・カテゴリのリスト。

表2-1に、NMTで使用されるネイティブ・メモリー・カテゴリを示します。これらのカテゴリは、リリースにより変更される可能性があります。

表2-1 ネイティブ・メモリー・トラッキングのメモリー・カテゴリ

カテゴリ

説明

Javaヒープ

オブジェクトが存在するヒープ

クラス

クラス・メタデータ

コード

生成されたコード

GC

カード表など、GCにより使用されるデータ

コンパイラ

コードを生成するときにコンパイラによって使用されるメモリー・トラッキング

記号

記号

メモリー・トラッキング

NMTによって使用されるメモリー。

空きチャンク・プール

アリーナのチャンク・プール内のチャンクによって使用されるメモリー

クラスの共有領域

クラス・データ共有アーカイブにマップされるメモリー

スレッド

スレッド・データ構造、リソース領域、ハンドル領域などを含む、スレッドによって使用されるメモリー。

スレッド・スタック

スレッド・スタック。コミットされたメモリーとしてマークされますが、OSによって完全にコミットされない場合があります

内部

コマンドライン・パーサー、JVMTI、プロパティおよびその他によって使用されるメモリーなど、前述のカテゴリに該当しないメモリー。

不明

メモリーのカテゴリを決定できない場合。

アリーナ: スタックまたは値オブジェクトとしてアリーナが使用されている場合

仮想メモリー: タイプ情報がまだ到着していない場合

ポストモーテム診断ツール

アプリケーションとJava HotSpot VMとの間で発生する問題のポストモーテム診断用に使用可能なツールとオプションのリスト。

表2-2に、ポストモーテム診断用に設計されたオプションおよびツールの要約を示します。アプリケーションがクラッシュした場合、これらのオプションやツールを使用して、クラッシュ発生時、または後でクラッシュ・ダンプからの情報を使用して追加情報を取得できます。

表2-2 ポストモーテム診断ツール

ツールまたはオプション

説明および使用方法

致命的エラー・ログ

回復不能(致命的)なエラーが発生すると、エラー・ログが作成されます。このファイルには、致命的エラーの発生時に取得された情報が含まれています。これは多くの場合、クラッシュ発生時に最初に調べる必要がある項目です。「致命的エラー・ログ」を参照してください。

-XX:+HeapDumpOnOutOfMemoryErrorオプション

このコマンド行オプションは、VMでネイティブのメモリー不足エラーが検出された際にヒープ・ダンプを生成することを指定します。「-XX:HeapDumpOnOutOfMemoryErrorオプション」を参照してください。

-XX:OnErrorオプション

このコマンド行オプションは、致命的エラーの発生時にユーザー指定の一連のスクリプトまたはコマンドを実行することを指定します。たとえばWindowsの場合、このオプションでクラッシュ・ダンプを強制するコマンドを実行できます。ポストモーテム・デバッガが構成されていないシステムでは、このオプションが非常に役立ちます。「-XX:OnErrorオプション」を参照してください。

-XX:+ShowMessageBoxOnErrorオプション

このコマンド行オプションは、致命的エラーの発生時にプロセスを中断します。このオプションはユーザーの応答に応じて、ネイティブ・デバッガ(dbx、gdb、msdev)を起動してVMに接続できます。「-XX:ShowMessageBoxOnErrorオプション」を参照してください。

その他の-XXオプション

トラブルシューティングに役立つ-XXコマンド行オプションは、ほかにもいくつかあります。「その他の-XX Optionsオプション」を参照してください。

jdbユーティリティ

デバッガ・サポートには、jdbなどのJava言語デバッガがコア・ファイルに接続することを許可する、AttachingConnectorが含まれています。これは、各スレッドがクラッシュ時に何をしていたのかを理解しようとする際に役立つ可能性があります。「jdbユーティリティ」を参照してください。

jinfoユーティリティ

(Oracle SolarisおよびLinuxオペレーティング・システムでのみ使用されるポストモーテム)

このユーティリティは、クラッシュから取得されたコア・ファイル、またはgcoreユーティリティを使用して取得されたコア・ファイルから、構成情報を取得できます。「jinfoユーティリティ」を参照してください。

jmapユーティリティ

(Oracle SolarisおよびLinuxオペレーティング・システムでのみ使用されるポストモーテム)

このユーティリティは、クラッシュから取得されたコア・ファイルから、またはgcoreユーティリティを使用して取得されたコア・ファイルから、ヒープ・ヒストグラムなどのメモリー・マップ情報を取得できます。「jmapユーティリティ」を参照してください。

jsadebugdデーモン

(Oracle SolarisおよびLinuxオペレーティング・システムのみ)

Serviceability Agent Debug Daemon (jsadebugd)は、Javaプロセスまたはコア・ファイルに接続し、デバッグ・サーバーとして動作します。「jsadebugdデーモン」を参照してください。

jstackユーティリティ

このユーティリティは、JavaプロセスからJavaおよびネイティブ・スタックの情報を取得できます。Oracle SolarisおよびLinuxオペレーティング・システムでは、コア・ファイルまたはリモート・デバッグ・サーバーからも情報を取得できます。「jstackユーティリティ」を参照してください。

ネイティブ・ツール

各オペレーティング・システムには、ポストモーテム診断に使用可能なネイティブ・ツールおよびユーティリティがあります。「オペレーティング・システムのネイティブ・ツール」を参照してください。

ハングアップ・プロセス・ツール

ハングアップ・プロセスでアプリケーションとJava HotSpot VMとの間で発生する問題を診断するためのツールとオプションのリスト。

表2-3では、ハングアップまたはデッドロックしたプロセスに関連するシナリオで役立つ可能性のあるオプションやツールをまとめています。これらのツールでは、アプリケーション起動時に特殊なオプションは必要ありません。

JVMおよびJavaアプリケーションに関する問題を診断するには、Java Mission Control、Java Flight Recorderおよびjcmdユーティリティを使用できます。診断機能を強化し、パフォーマンスのオーバーヘッドを削減するには、以前のjstack,、jinfoおよびjmapユーティリティのかわりに最新の診断ツールjcmdの使用をお薦めします。

表2-3 ハングアップ・プロセス・ツール

ツールまたはオプション

説明および使用方法

Ctrl+Breakハンドラ

(Oracle SolarisおよびLinuxオペレーティング・システムでは[Ctrl]+[\]またはkill -QUIT pid、Windowsでは[Ctrl]+[Break])

このキーの組合せでは、スレッド・ダンプとデッドロック検出が実行されます。Ctrl+Breakハンドラではオプションで、並行ロックとその所有者の一覧や、ヒープ・ヒストグラムを出力することもできます。「Ctrl+Breakハンドラ」を参照してください。

jcmdユーティリティ

このユーティリティを使用してJVMに診断コマンド・リクエストを送信し、JVMではこれらのリクエストを使用してJavaフライト記録(JFR)を制御します。JFRはフライト記録のイベントのトラブルシューティングと診断に使用します。「jcmdユーティリティ」を参照してください。

jdbユーティリティ

デバッガ・サポートには、jdbなどのJava言語デバッガがプロセスに接続することを許可する、接続コネクタが含まれています。これは、ハング・アップやデッドロックの発生時に各スレッドが何をしていたのかを示すのに役立つ可能性があります。「jdbユーティリティ」を参照してください。

jinfoユーティリティ

このユーティリティはJavaプロセスから構成情報を取得できます。「jinfoユーティリティ」を参照してください。

jmapユーティリティ

このユーティリティは、Javaプロセスからヒープ・ヒストグラムなどのメモリー・マップ情報を取得できます。Oracle SolarisおよびLinuxオペレーティング・システムでは、プロセスがハングアップした場合に-Fオプションを使用できます。「jmapユーティリティ」を参照してください。

jsadebugdデーモン

(Oracle SolarisおよびLinuxオペレーティング・システムのみ)

Serviceability Agent Debug Daemon (jsadebugd)は、Javaプロセスまたはコア・ファイルに接続し、デバッグ・サーバーとして動作します。「jsadebugdデーモン」を参照してください。

jstackユーティリティ

このユーティリティは、JavaプロセスからJavaおよびネイティブ・スタックの情報を取得できます。「jstackユーティリティ」を参照してください。

ネイティブ・ツール

各オペレーティング・システムには、ハング・アップやデッドロックの状況で役立つ可能性のあるネイティブ・ツールおよびユーティリティがあります。「オペレーティング・システムのネイティブ・ツール」を参照してください。

モニタリング・ツール

実行中のアプリケーションをモニターして問題を検知するためのツールおよびオプションのリスト。

表2-4に示すツールは、実行中のアプリケーションのモニター用に設計されています。

JVMおよびJavaアプリケーションに関する問題を診断するには、Java Mission Control、Java Flight Recorderおよびjcmdユーティリティを使用できます。診断機能を強化し、パフォーマンスのオーバーヘッドを削減するには、以前のjstack,、jinfoおよびjmapユーティリティのかわりに最新の診断ツールjcmdの使用をお薦めします。

表2-4 モニタリング・ツール

ツールまたはオプション

説明および使用方法

Java Mission Control

Java Mission Control (JMC)は、HotSpot JVM用の新しいJDKプロファイリングおよび診断ツール・プラットフォームです。基本的なモニタリングと管理、および本番稼働時のプロファイリングと診断を行う高性能なツール・スイートです。Java Mission Controlは、プロファイリング・ツールにつきもののパフォーマンス・オーバーヘッドの問題を最小限に抑えます。

jcmdユーティリティ

このユーティリティを使用してJVMに診断コマンド・リクエストを送信し、JVMではこれらのリクエストを使用してJavaフライト記録を制御します。JFRは、フライト記録のイベントを使用してJVMおよびJavaアプリケーションのトラブルシューティングと診断に使用されます。「jcmdユーティリティ」を参照してください。

JConsoleユーティリティ

このユーティリティは、Java Management Extensions (JMX)に基づくモニタリング・ツールです。このツールは、Java仮想マシンの組込みJMXインストゥルメンテーションを使用して、実行中のアプリケーションのパフォーマンスやリソース消費に関する情報を提供します。「JConsole」を参照してください。

jmapユーティリティ

このユーティリティは、Javaプロセス、コア・ファイル、またはリモート・デバッグ・サーバーから、ヒープ・ヒストグラムなどのメモリー・マップ情報を取得できます。「jmapユーティリティ」を参照してください。

jpsユーティリティ

このユーティリティは、ターゲット・システム上で計測されたJava HotSpot VMを一覧表示します。VMが埋め込まれた環境、つまりjava起動ツールではなくJNI呼び出しAPIを使ってVMが起動される環境では、このユーティリティが非常に役立ちます。「jpsユーティリティ」を参照してください。

jstackユーティリティ

このユーティリティは、JavaプロセスからJavaおよびネイティブ・スタックの情報を取得できます。Oracle SolarisおよびLinuxオペレーティング・システムでは、コア・ファイルまたはリモート・デバッグ・サーバーからも情報を取得できます。「jstackユーティリティ」を参照してください。

jstatユーティリティ

このユーティリティは、Javaの組込みインストゥルメンテーションを使用して、実行中アプリケーションのパフォーマンスやリソース消費に関する情報を提供します。このツールは、パフォーマンスの問題、特にヒープ・サイズ決定やガベージ・コレクションに関係する問題を診断するときに使用できます。「jstatユーティリティ」を参照してください。

jstatdデーモン

このユーティリティは、計測されたJava仮想マシンの作成と終了をモニターし、ローカル・ホスト上で実行されているVMにリモート・モニタリング・ツールが接続することを許可するインタフェースを提供する、Remote Method Invocation (RMI)サーバー・アプリケーションです。「jstatdデーモン」を参照してください。

visualgcユーティリティ

このユーティリティは、ガベージ・コレクション・システムのグラフィカル表示を提供します。これはjstatと同様に、Java HotSpot VMの組込みインストゥルメンテーションを使用します。「visualgcツール」を参照してください。

ネイティブ・ツール

各オペレーティング・システムには、モニタリング目的に役立つ可能性のあるネイティブ・ツールおよびユーティリティがあります。たとえば、Oracle Solaris 10オペレーティング・システムで導入された動的トレース(DTrace)機能は、高度なモニタリングを実行します。「オペレーティング・システムのネイティブ・ツール」を参照してください。

その他のツール、オプション、変数、およびプロパティ

問題の診断に役立つ一般的なトラブルシューティングのツール、オプション、変数およびプロパティのリスト。

特定タイプの問題向けに設計されたツールに加え、表2-5に示すツール、オプション、変数、およびプロパティは、その他の問題を診断するのに役立ちます。

JVMおよびJavaアプリケーションに関する問題を診断するには、Java Mission Control、Java Flight Recorderおよびjcmdユーティリティを使用できます。診断機能を強化し、パフォーマンスのオーバーヘッドを削減するには、以前のjstack,、jinfoおよびjmapユーティリティのかわりに最新の診断ツールjcmdの使用をお薦めします。

表2-5 一般的なトラブルシューティング・ツールおよびオプション

ツールまたはオプション

説明および使用方法

Java Mission Control

Java Mission Control (JMC)は、HotSpot JVM用の新しいJDKプロファイリングおよび診断ツール・プラットフォームです。基本的なモニタリングと管理、および本番稼働時のプロファイリングと診断を行う高性能なツール・スイートです。Java Mission Controlは、プロファイリング・ツールにつきもののパフォーマンス・オーバーヘッドの問題を最小限に抑えます。「Java Mission Control」を参照してください。

jcmdユーティリティ

このユーティリティを使用してJVMに診断コマンド・リクエストを送信し、JVMではこれらのリクエストを使用してJavaフライト記録(JFR)を制御します。JFRは、フライト記録のイベントを使用してJVMおよびJavaアプリケーションのトラブルシューティングと診断に使用されます。

jinfoユーティリティ

このユーティリティは、指定されたJavaプロセスの特定のJVMフラグの値を動的に設定、設定解除および変更できます。Oracle SolarisおよびLinuxオペレーティング・システムでは、構成情報も出力できます。

jrunscriptユーティリティ

このユーティリティはコマンド行スクリプト・シェルで、インタラクティブおよびバッチ・モードの両方のスクリプト実行をサポートしています。

Oracle Solaris Studio dbxデバッガ

これはインタラクティブなコマンド行デバッグ・ツールで、プログラムを停止してその状態を調べるなど、プログラムの動的実行を完全に制御できます。詳細は、Oracle Solaris Studioプログラム・デバッギングにあるdbxの最新ドキュメントを参照してください。

Oracle Solaris Studioパフォーマンス・アナライザ

このツールを使って、コードのパフォーマンスを評価し、潜在的なパフォーマンス問題を特定し、コード内で問題が発生する部分を突きとめることが容易になる可能性があります。Performance Analyzerはコマンド行、またはグラフィカル・ユーザー・インタフェースから使用できます。詳細は、Oracle Solaris Studioパフォーマンス・アナライザを参照してください。

Sunのデータ領域プロファイリング: DProfile

このツールは、Sunコンピューティング・システム内でのデータ・フローを洞察することで、ソフトウェアとハードウェアの両方のボトルネックを特定するのを支援します。DProfileは、Sun Studio 11コンパイラ・スイートでPerformance Analyzer GUI経由でサポートされています。DTraceまたは動的トレース診断ツールに関する項を参照してください。

-Xcheck:jniオプション

このオプションは、Java Native Interface (JNI)を使用する、またはサード・パーティのライブラリ(一部のJDBCドライバなど)を採用するアプリケーションの問題を診断する際に役立ちます。「-Xcheck:jniオプション」を参照してください。

-verbose:classオプション

このオプションは、クラスのロードとアンロードのロギングを有効にします。「-verbose:classオプション」を参照してください。

-verbose:gcオプション

このオプションは、ガベージ・コレクション情報のロギングを有効にします。「-verbose:gcオプション」を参照してください。

-verbose:jniオプション

このオプションは、JNIのロギングを有効にします。「-verbose:jniオプション」を参照してください。

JAVA_TOOL_OPTIONS環境変数

この環境変数では、ツールの初期化(具体的には-agentlibまたは-javaagentオプションを使用したネイティブまたはJavaプログラミング言語エージェントの起動)を指定できます。「環境変数とシステム・プロパティ」を参照してください。

java.security.debugシステム・プロパティ

このシステム・プロパティは、JavaのJREのセキュリティ・チェックが実行中にトレース・メッセージを出力するかどうかを制御します。「java.security.debugシステム・プロパティ」を参照してください。

java.lang.managementパッケージ

java.lang.managementパッケージは、JVMやオペレーティング・システムをモニターおよび管理するための管理インタフェースを提供します。

具体的には、次の各システムのインタフェースがカバーされています。

- クラスのロード

- コンパイル

- ガベージ・コレクション

- メモリー・マネージャ

- ランタイム

- スレッド

JDKには、java.lang.managementパッケージの使用方法を示すコード例が含まれています。これらの例は$JAVA_HOME/demo/managemenディレクトリにあります。コード例の一部を次に示します。

- MemoryMonitorは、java.lang.management APIを使ってアプリケーションで消費されるすべてのメモリー・プールのメモリー使用量をモニターする方法を示します。

- FullThreadDumpは、java.lang.management APIを使って完全なスレッド・ダンプを取得し、プログラムでデッドロックを検出する方法を示します。

- VerboseGCは、java.lang.management APIを使ってアプリケーションのガベージ・コレクション統計やメモリー使用量を出力する方法を示します。

JDKリリースにはjava.lang.managementパッケージのほかに、プラットフォーム拡張がcom.sun.managementパッケージとして含まれています。プラットフォーム拡張には、コレクションをサイクルで実行するガベージ・コレクタから詳細統計を取得するための管理インタフェースが含まれています。これらの拡張には、オペレーティング・システムから追加のメモリー統計を取得するための管理インタフェースも含まれています。

java.lang.instrumentパッケージ

java.lang.instrumentパッケージは、Javaプログラミング言語エージェントがJVMで実行中のプログラムを計測することを許可するサービスを提供します。

インストゥルメンテーションは、プロファイラなどのツールや、メソッド呼出しをトレースするためのツールなどで使用されます。このパッケージを使えば、ロード時および動的計測の両方が容易になります。また、ロード済クラスに関する情報や特定のオブジェクトが消費するストレージ量に関する情報を取得するメソッドも含まれています。

java.lang.Threadクラス

java.lang.Threadクラスには、すべてのライブ・メソッドのスタック・トレースのマップを返す、getAllStackTracesと呼ばれるstaticメソッドが含まれています。

Threadクラスには、スレッドの状態を返す、getStateと呼ばれるメソッドも含まれています。スレッドの状態はjava.lang.Thread.State列挙型で定義されます。これらのメソッドは、診断またはモニタリング機能をアプリケーションに追加する際に役立つことがあります。

JVM Tool Interface

JVM Tools Interface (JVM TI)とは、様々な開発ツールやモニタリング・ツールで使用できるネイティブ(C/C++)プログラミング・インタフェースです。

JVM TIは、VMの状態にアクセスする必要のある各種ツール(プロファイリング・ツール、デバッグ・ツール、モニタリング・ツール、スレッド分析ツール、カバレージ分析ツールを含むがこれに限らない)のためのインタフェースを提供します。

JVM TIに依存するエージェントのいくつかの例を次に示します。

- Java Debug Wire Protocol (JDWP)

- java.lang.instrumentパッケージ

JVM TIの仕様は、JVM Tool Interfaceのドキュメントで参照できます。

JDKには、JVM TIの使用方法を示すコード例が含まれています。これらの例は$JAVA_HOME/demo/jvmtiディレクトリにあります。コード例の一部を次に示します。

- mtraceは、メソッドの呼出しおよび復帰回数を追跡するエージェント・ライブラリです。これは、バイトコード・インストゥルメンテーションを使って仮想マシンにロードされたすべてのクラスを計測し、使用頻度の高いメソッドのソート済みリストを出力します。

- heapTrackerは、オブジェクトの割当てを追跡するエージェント・ライブラリです。これは、バイトコード・インストゥルメンテーションを使ってコンストラクタ・メソッドを計測します。

- heapViewerは、Ctrl+Breakハンドラが呼び出されたときにヒープ統計を出力するエージェント・ライブラリです。「Ctrl+Breakハンドラ」を参照してください。ロードされたクラスごとに、そのクラスのインスタンス数と使用済容量を出力します。

jrunscriptユーティリティ

jrunscriptユーティリティはコマンド行スクリプト・シェルです。

インタラクティブ・モード、バッチ・モードの両方のスクリプト実行がサポートされています。このシェルではデフォルトでJavaScriptが使用されますが、他の任意のスクリプト言語を指定することもできます。そのためには、そのスクリプト・エンジンの.classファイルを含むJARファイルへのパスを指定します。

jrunscriptユーティリティは、Java言語とスクリプト言語との通信によって探求的なプログラミング・スタイルをサポートしています。

jsadebugdデーモン

Javaサービスアビリティ・エージェント・デバッグ・デーモン(jsadebugd)は、Javaプロセスまたはコア・ファイルに接続し、デバッグ・サーバーとして動作します。

このユーティリティは現在のところ、Oracle SolarisおよびLinuxオペレーティング・システムでのみ利用できます。jstack、jmapおよびjinfoなどのリモート・クライアントは、Java Remote Method Invocation (RMI)を使用しているサーバーに接続できます。

jstatdデーモン

jstatdデーモンは、計測された各Java HotSpotの作成と終了をモニターし、ローカル・ホスト上で実行されているJVMにリモート・モニタリング・ツールが接続できるようにするインタフェースを提供する、RMIサーバー・アプリケーションです。

たとえば、このデーモンを使えば、jpsユーティリティでリモート・システム上のプロセスを一覧表示できます。

注意:

FAT32ファイル・システム上では、インストゥルメンテーションにアクセスできません。

スレッド・ダンプのスレッド状態

スレッド・ダンプに表示される可能性があるスレッド状態のリスト。

表2-6は、Ctrl+Breakハンドラによるスレッド・ダンプで可能なスレッド状態を示しています。

表2-6 スレッド・ダンプのスレッド状態

スレッド状態

説明

NEW

スレッドはまだ開始されていません。

RUNNABLE

スレッドはJVM内で実行されています。

BLOCKED

スレッドはモニター・ロックを待機してブロックされています。

WAITING

スレッドは、別のスレッドが特定のアクションを実行するのを無期限に待機しています。

TIMED_WAITING

スレッドは指定された待機時間、別のスレッドがアクションを実行するのを待機しています。

TERMINATED

スレッドは終了しています。

オペレーティング・システムに基づくトラブルシューティング・ツール

問題のトラブルシューティングに利用できるWindowsのネイティブ・ツールのリスト。

表2-7は、Windowsオペレーティング・システムで使用可能なトラブルシューティング・ツールの一覧を示しています。

表2-7 Windowsのネイティブ・トラブルシューティング・ツール

ツール

説明

dumpchk

メモリー・ダンプ・ファイルが正常に作成されたことを確認するコマンド行ユーティリティ。このツールは、MicrosoftのWebサイトから入手可能なDebugging Tools for Windowsダウンロードに含まれています。「Windowsでのクラッシュ・ダンプの収集」を参照してください。

msdevデバッガ

Visual C++およびWin32デバッガの起動に使用可能なコマンド行ユーティリティ。

userdump

User Mode Process Dumperは、MicrosoftのWebサイトから入手可能なOEM Support Toolsダウンロードに含まれています。「Windowsでのクラッシュ・ダンプの収集」を参照してください。

windbg

Windowsデバッガは、Windowsアプリケーションまたはクラッシュ・ダンプのデバッグに使用できます。このツールは、MicrosoftのWebサイトから入手可能なDebugging Tools for Windowsダウンロードに含まれています。「Windowsでのクラッシュ・ダンプの収集」を参照してください。

/Mdおよび/Mddコンパイラ・オプション

メモリー割当てを追跡するための追加サポートを自動的に含めるコンパイラ・オプション。

表2-8では、Linuxオペレーティング・システム・バージョン10で導入または改善されたいくつかのトラブルシューティング・ツールについて説明しています。

表2-8 Linuxのネイティブ・トラブルシューティング・ツール

ツール

説明

c++filt

C++分解シンボル名を分解解除します。このユーティリティは、Linux上のgccというネイティブC++コンパイラ・スイートとともに配布されます。

gdb

GNUデバッガ

libnjamd

メモリー割当ての追跡

lsstack

スレッド・スタックを出力します(Oracle Solarisオペレーティング・システムのpstackに類似)

すべてのディストリビューションがデフォルトでこのツールを提供しているわけではありません。したがって、オープン・ソース・ダウンロードからのダウンロードが必要になる場合があります。

ltrace

ライブラリ呼出しトレーサ(Oracle Solarisオペレーティング・システムのtruss -uと同等)

すべてのディストリビューションがデフォルトでこのツールを提供しているわけではありません。したがって、オープン・ソース・ダウンロードからのダウンロードが必要になる場合があります。

mtraceおよびmuntrace

GNU mallocトレーサ

pmapやpstackなどのprocツール

Oracle Solarisオペレーティング・システムのprocツールの一部(すべてではない)にはLinuxと同等のツールが含まれています。Linuxでのコア・ファイルのサポートはOracle Solarisオペレーティング・システムには及ばず、たとえば、pstackをコア・ダンプには使用できません。

strace

システム・コール・トレーサ(Oracle Solarisオペレーティング・システムのtruss -tと同等)

top

CPU負荷が非常に高いプロセスを表示します。

vmstat

プロセス、メモリー、ページング、ブロックI/O、トラップ、およびCPUアクティビティに関する情報を報告します。

表2-9は、Oracle Solarisオペレーティング・システムで使用可能なトラブルシューティング・ツールの一覧を示しています。

表2-9 Oracle Solarisオペレーティング・システムのネイティブ・トラブルシューティング・ツール

ツール

説明

coreadm

JVMによって生成されるコア・ファイルの名前と場所を指定します。

cpustat

CPUパフォーマンス・カウンタを使ってシステム動作をモニターします。

cputrack

CPUパフォーマンス・カウンタを使用してプロセスとLWPの動作をモニターします。

c++filt

C++分解シンボル名を分解解除します。このユーティリティは、Oracle Solarisオペレーティング・システム上のSUNWsproというネイティブC++コンパイラ・スイートとともに配布されます。

dtrace

DTraceは、Oracle Solaris 10オペレーティング・システムで導入された、動的トレース・コンパイラおよびトレース・ユーティリティです。これは、カーネル関数、システム・コール、およびユーザー関数の動的トレースを実行できます。このツールを使えば、入口、出口、およびその他のプローブ・ポイントで任意の安全なスクリプトを実行できます。スクリプトは、Dプログラミング言語と呼ばれる、Cに似ているが安全なポインタ・セマンティクス言語で記述されます。「DTraceツール」も参照してください。

gcore

プロセスのコア・ダンプを強制します。コア・ダンプの書込み後、プロセスは継続します。

intrstat

割込みスレッドで消費されるCPUに関する統計を報告します。

iostat

I/O統計を報告します。

libumem

このライブラリは、Oracle Solaris 9オペレーティング・システムupdate 3で導入され、高速かつスケーラブルなオブジェクト・キャッシュ用メモリー割当ておよび拡張デバッグ・サポートを提供します。このツールでは、メモリー管理のバグを検出して修正できます。「libumemツールによるリークの検出」を参照してください。

mdb

カーネルおよびユーザー・アプリケーションとクラッシュ・ダンプ用のモジュラ・デバッガ

netstat

各種ネットワーク関連データ構造体の内容を表示します。

pargs

プロセス引数、環境変数または補助ベクトルを出力します。ほかのコマンド(psなど)のように、長い出力が切り詰められることはありません。

pfiles

プロセス・ファイル記述子に関する情報を出力します。Oracle Solaris 10オペレーティング・システム以降、ファイル名も出力されるようになりました。

pldd

プロセスによってロードされた共有オブジェクトを出力します。

pmap

プロセスまたはコア・ファイルのメモリー・レイアウト(ヒープ、データおよびテキスト・セクションを含む)を出力します。Oracle Solaris 10以降は、スタック・セグメントがテキスト[stack]とスレッドIDで明確に識別されます。「pmapユーティリティの改善」を参照してください。

prstat

アクティブなOracle Solarisオペレーティング・システム・プロセスの統計を報告します。(topに類似)

prun

プロセスを実行モードに設定します(pstopの逆)。

ps

すべてのプロセスを一覧表示します。

psig

プロセスのシグナル・ハンドラを一覧表示します。

pstack

特定のプロセスまたはコア・ファイルのスレッドのスタックを出力します。Oracle Solaris 10オペレーティング・システム以降は、JavaフレームでJavaメソッド名を出力できます。「pstackユーティリティの改善」を参照してください。

pstop

プロセスを停止(中断)します。

ptree

指定されたPIDを含むプロセス・ツリーを出力します。

sar

システム・アクティビティ・レポータ

sdtprocess

CPU負荷が非常に高いプロセスを表示します(topに類似)。

sdtperfmeter

システム・パフォーマンス(CPU、ディスク、ネットワークなど)を示すグラフを表示します。

top

CPU負荷が非常に高いプロセスを表示します。このツールは、Oracle Solarisオペレーティング・システムではフリーウェアとして利用可能ですが、デフォルトではインストールされません。

trapstat

実行時トラップ統計(SPARCのみ)を表示します。

truss

システム・コール、ユーザー・モード関数、およびシグナルの入口および出口イベントをトレースし、オプションでこれらのイベントのいずれかでプロセスを停止します。このツールは、システム・コールやユーザー関数の引数も出力します。

vmstat

システム仮想メモリーの統計を報告します。

watchmalloc

メモリー割当てを追跡します。

3 メモリー・リークのトラブルシューティング

この章では、メモリー・リークが関与する問題の診断について、いくつかのアドバイスを行います。

アプリケーションの実行時間がしだいに長くなる場合や、オペレーティング・システムの実行速度がしだいに低下しているように思われる場合、これはメモリー・リークの兆候である可能性があります。つまり、仮想メモリーが割り当てられているけれども、それが不要になったときに返されません。最終的には、アプリケーションまたはシステムがメモリーを使い果たし、アプリケーションが異常終了します。

この章の構成は、次のとおりです。

- Javaフライト・レコーダを使用したメモリー・リークのデバッグ

- OutOfMemoryError例外の理解

- OutOfMemoryErrorのかわりにクラッシュのトラブルシューティング

- Java言語コードでのリークの診断

- ネイティブ・コードでのリークの診断

Javaフライト・レコーダによるメモリー・リークのデバッグ

Java Flight Recorder (JFR)は商用機能です。開発者のデスクトップまたはラップトップ上では無償で、また評価目的であればテスト環境、開発環境および本番環境で使用できます。

ただし、JFRを本番サーバーで有効にする場合は、商用ライセンスが必要です。それ以外の目的でJava Mission Control (JMC)をJDKで使用する場合、商用ライセンスは必要ではありません。

JFRの商用機能と入手方法の詳細は、製品マニュアルを参照してください。

JFRの商用ライセンスの詳細は、ライセンス契約を参照してください。

次の項では、図を示して、Javaフライト・レコーダでメモリー・リークをデバッグする方法を説明します。

- メモリー・リークの検出

- リーク・クラスの調査

- リークの調査

メモリー・リークの検出

Javaフライト記録を使用してメモリー・リークを早期に検出し、OutOfmemoryErrorsを防止します。

スロー・メモリー・リークの検出は難しい場合があります。一般的な症状は、頻繁なガベージ・コレクションにより実行に時間がかかり、その後、アプリケーションが低速になります。やがてOutOfmemoryErrorsが表示される可能性があります。ただし、Javaフライト・レコーダを使用すると、問題が発生する前であっても、メモリー・リークを早期に検出できます。

アプリケーションのライブ・セットの経時的な増加がないか確認します。ライブ・セットは、Oldコレクションの後(すべての非ライブ・オブジェクトがガベージ・コレクトされた後)に使用されるJavaヒープ容量です。ライブ・セットは、様々な方法で調査できます。たとえば、-verbosegcオプションを使用して実行するか、JMC JMXコンソールを使用してJVMに接続し、com.sun.management.GarbageCollectorAggregator MBeanを見ます。しかし、別の簡単なアプローチは、フライト記録を取ることです。

記録を開始するときにヒープ統計を有効にすると、記録の開始時と終了時にOldコレクションがトリガーされます。これにより、アプリケーションに若干の遅延が生じる場合があります。しかし、ヒープ統計は、正確なライブ・セット情報を生成します。やや急速なメモリー・リークが疑われる場合は、たとえば、1時間以上実行されるプロファイリング記録を取ります。図3-1に示すように、「メモリー」タブをクリックし、「ガベージ・コレクション」タブを選択して、最初と最後のOldコレクションを確認します。

図3-1 メモリー・リークのデバッグ - 「ガベージ・コレクション」タブ

図3-1,に示すように、最初のOldコレクションを選択して、GC後のヒープ・データとヒープ使用量を調べます。この記録では、34.10MBです。今度は、最後のOldコレクションからの同じデータを調べて、ライブ・セットが増加したかどうかを確認します。記録を取る前に、アプリケーションが起動し、安定した状態に到達できるようにする必要があります。

ゆっくりしたリークの場合は、5分間の短時間の記録を取ることができます。次に、たとえば、(メモリー・リークの速さに応じて)24時間後に、もう1回記録を取ります。明らかに、ライブ・セットは上下しますが、時間の経過とともに着実に増加している場合は、メモリー・リークの可能性があります。

リーク・クラスの調査

Javaフライト記録を使用して、メモリー・リークを特定します。

記録がリークを示したら、「オブジェクト統計」を調べます。1つの長い記録を検討し、記録を通してヒープ使用量が最も増加したクラスを調べます。間隔を置いて複数の記録を取った場合は、図3-2に示すように、「ヒープの内容」セクションを比較し、どのオブジェクト・タイプが記録間で最も増加しているかを調べます。

図3-2 メモリー・リークのデバッグ - リーク・クラスの調査

特に、標準ライブラリの一部ではないクラスに注目してください。たとえば、多くの場合、最大の増加率の1つとしてChar配列が表示されます。これは、多くのStringsが割り当てられているためです。したがって、これらのStringsを維持しているオブジェクトに注意します。10個のStringsをメンバーとして持つクラスがある場合、オブジェクト自体がヒープを使いすぎることはありません。ヒープは、主にChar配列へのポインタが含まれているStringsによって使用されます。したがって、オブジェクトのサイズではなく、インスタンスの数でソートするのが適切です。いずれかのアプリケーション・クラスに多くのインスタンスがある場合、それらのオブジェクトが他のオブジェクトを維持している可能性があります。

リークの調査

Javaフライト記録を使用した追加情報を使用してメモリー・リークを特定するためのヒント。

Javaフライト記録を使用して、いくつかの追加情報を得ることができます。

図3-3に示すように、「割当て」サブタブで、オブジェクトの割当先のサンプルを見ます。

図3-3 メモリー・リークのデバッグ - 「割当て」タブ

特定のクラスのリークが予想される場合は、「新しいTLABの割当て」タブを見てください。割り当てられているクラス・サンプルをチェックします。スロー・リークの場合は、このオブジェクトの割当てが少なく、サンプルがないこともあります。また、特定の割当てサイトのみがリークの原因となっている可能性もあります。要約すると、これによってリークの適切な割当てスタック・トレースに到達することは保証されませんが、重要な手がかりを得る可能性があります。

OutOfMemoryError例外の理解

java.lang.OutOfMemoryErrorのエラーは、Javaヒープにオブジェクトを割り当てるための十分な空間がないときにスローされます。

メモリー・リークの一般的な兆候の1つは、java.lang.OutOfMemoryError例外です。この場合、ガベージ・コレクタによって新しいオブジェクトを格納するための空間を確保することも、ヒープをこれ以上拡張することもできません。また、このエラーは、Javaクラスのロードをサポートするための十分なネイティブ・メモリーがないときにスローされる場合もあります。ガベージ・コレクションの実行に過剰な時間が消費され、メモリーがほとんど解放されていない場合、まれにjava.lang.OutOfMemoryErrorがスローされることがあります。

java.lang.OutOfMemoryError例外がスローされるときに、スタック・トレースも出力されます。

java.lang.OutOfMemoryError例外は、ネイティブ割当てを満たすことができないとき(たとえば、スワップ空間が少ない場合)に、ネイティブ・ライブラリ・コードによってスローされることもあります。

OutOfMemoryError例外を診断する最初の手順は、その例外の原因を突き止めることです。スローされた理由が、Javaヒープがいっぱいであるからなのか、それともネイティブ・ヒープがいっぱいであるからなのかを調べます。原因を見つけることができるように、例外テキストの最後に詳細なメッセージが含まれています(次の例外を参照)。

Exception in thread thread_name: java.lang.OutOfMemoryError: Java heap space

原因: Java heap spaceという詳細メッセージは、Javaヒープ内でオブジェクトを割り当てることができなかったことを示しています。このエラーは、必ずしもメモリー・リークを意味しません。この問題は、指定されたヒープ・サイズ(指定されていない場合はデフォルト・サイズ)がアプリケーションにとって十分でないという、単純な構成の問題である可能性があります。

それ以外の場合、特に長期にわたって稼動するアプリケーションでは、このメッセージはアプリケーションが誤ってオブジェクトへの参照を保持しているため、オブジェクトのガベージ・コレクションができないことを示している可能性があります。これは、Java言語ではメモリー・リークに相当します。注意: アプリケーションによって呼び出されたAPIが誤ってオブジェクト参照を保持している可能性もあります。

このエラーのもう1つの潜在的な原因は、ファイナライザを過度に使用するアプリケーションで発生します。クラスにfinalizeメソッドがある場合、そのタイプのオブジェクトの空間はガベージ・コレクション時に再利用されません。かわりに、ガベージ・コレクション後にファイナライズ用のキューにオブジェクトが入れられ、後でファイナライズが実行されます。Oracle Sunの実装では、ファイナライザはファイナライズ・キューを提供するデーモン・スレッドによって実行されます。ファイナライザ・スレッドがファイナライズ・キューを処理しきれなかった場合は、Javaヒープがいっぱいになる可能性があり、このタイプのOutOfMemoryError例外がスローされます。この状況を発生させるシナリオの1つは、アプリケーションが優先度の高いスレッドを作成しているため、ファイナライザ・スレッドがファイナライズ・キューを処理する速度よりキューの増加速度の方が速くなっている場合です。

処置: ファイナライズが保留されているオブジェクトのモニター方法については、「ファイナライズを保留中のオブジェクトのモニター」を参照してください。

Exception in thread thread_name: java.lang.OutOfMemoryError: GC Overhead limit exceeded

原因: 「GC overhead limit exceeded」という詳細メッセージは、ガベージ・コレクタが常時実行されているため、Javaプログラムの処理がほとんど進んでいないことを示しています。ガベージ・コレクション後、Javaプロセスでガベージ・コレクションの実行に約98%を超える時間が費やされ、ヒープのリカバリが2%未満であり、その状態が直近5回(コンパイル時間定数)の連続するガベージ・コレクションで続いている場合に、java.lang.OutOfMemoryErrorがスローされます。この例外は通常、Javaヒープに新しい割当て用の空き領域がわずかしかなく、ライブ・データの容量をほとんど格納できないためにスローされます。

処置: ヒープ・サイズを増やしてください。GC Overhead limit exceededのjava.lang.OutOfMemoryError例外は、コマンド行フラグ-XX:-UseGCOverheadLimitを使用すると無効にできます。

Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit

原因: 「Requested array size exceeds VM limit」という詳細メッセージは、アプリケーション(またはそのアプリケーションが使用するAPI)がヒープ・サイズより大きい配列を割り当てようとしたことを示しています。たとえば、アプリケーションが512MBの配列を割り当てようとしたが、最大ヒープ・サイズが256MBだった場合は、「Requested array size exceeds VM limit」という理由とともにOutOfMemoryErrorがスローされます。

処置: 通常、この問題は構成の問題(ヒープ・サイズが小さすぎる)であるか、アプリケーションが非常に大きい配列を作成しようとする結果をもたらすバグ(たとえば、不正なサイズを計算するアルゴリズムを使用して配列内の要素の数が計算された場合など)です。

Exception in thread thread_name: java.lang.OutOfMemoryError: Metaspace

原因: Javaクラス・メタデータ(Javaクラスの仮想マシン内部表現)がネイティブ・メモリー(ここでは、メタスペースと呼ばれる)に割り当てられています。クラス・メタデータのメタスペースが枯渇すると、「MetaSpace」という詳細のjava.lang.OutOfMemoryError例外がスローされます。クラス・メタデータに使用できるメタスペース容量は、コマンド行に指定するMaxMetaSpaceSizeパラメータにより制限されます。クラス・メタデータに必要なネイティブ・メモリー容量がMaxMetaSpaceSizeを超過すると、MetaSpace詳細とともにjava.lang.OutOfMemoryError例外がスローされます。

処置: コマンド行でMaxMetaSpaceSizeが設定されている場合は、その値を増やしてください。MetaSpaceは、Javaヒープと同じアドレス空間から割り当てられます。Javaヒープのサイズを減らすと、MetaSpaceに使用できる領域が増えます。このトレードオフが成り立つのは、Javaヒープに余分な空き領域がある場合のみです。次に示す「Out of swap space」詳細メッセージの「処置」を参照してください。

Exception in thread thread_name: java.lang.OutOfMemoryError: request size bytes for reason.Out of swap space?

原因: 「request size bytes for reason.Out of swap space?」という詳細メッセージは、OutOfMemoryError例外であるように見えます。しかし、ネイティブ・ヒープからの割当てが失敗し、ネイティブ・ヒープが枯渇寸前になっている可能性があるときに、Java HotSpot VMコードはこの見かけ上の例外を報告します。このメッセージは、失敗した要求のサイズ(バイト数)とメモリー要求の理由を示しています。通常、その理由は割当ての失敗を報告するソース・モジュールの名前になりますが、場合によっては実際の理由になることもあります。

処置: このエラー・メッセージがスローされると、VMは致命的エラー処理メカニズムを呼び出します(つまり、クラッシュ発生時のスレッド、プロセス、およびシステムに関する有用な情報を含む、致命的エラー・ログ・ファイルを生成します)。ネイティブ・ヒープ不足の場合は、ログ内のヒープ・メモリーとメモリー・マップの情報が役立つ可能性があります。「致命的エラー・ログ」を参照してください。

このタイプのOutOfMemoryError例外がスローされた場合は、オペレーティング・システムのトラブルシューティング・ユーティリティを使用して、問題をより詳しく診断する必要がある場合があります。「オペレーティング・システムのネイティブ・ツール」を参照してください。

Exception in thread thread_name: java.lang.OutOfMemoryError: Compressed class space

原因: 64ビット・プラットフォーム上で、クラス・メタデータへのポインタが32ビットのオフセットで表現されている可能性があります(UseCompressedOopsを使用)。これはコマンド行フラグUseCompressedClassPointersで制御されます(デフォルトの場合)。UseCompressedClassPointersが使用されている場合、クラス・メタデータに使用できる領域量はCompressedClassSpaceSizeの容量で固定されます。UseCompressedClassPointersに必要な領域がCompressedClassSpaceSizeを超える場合、「Compressed class space」という詳細のjava.lang.OutOfMemoryErrorがスローされます。

処置: CompressedClassSpaceSizeを増やして、UseCompressedClassPointersをオフにしてください。注意: CompressedClassSpaceSizeの許容サイズには制限があります。たとえば、-XX: CompressedClassSpaceSize=4gが許容制限を超えると、次のようなメッセージが表示されます。

CompressedClassSpaceSize of 4294967296 is invalid; must be between 1048576 and 3221225472. (CompressedClassSpaceSizeの4294967296は無効です。1048576から3221225472の間に設定する必要があります。)

注意:
クラス・メタデータには複数の種類(-klassメタデータやその他のメタデータ)があります。CompressedClassSpaceSizeによって制限された領域に格納されるのは、klassメタデータのみです。その他のメタデータはMetaspaceに格納されます。

Exception in thread thread_name: java.lang.OutOfMemoryError: reason stack_trace_with_native_method

原因: エラー・メッセージの詳細部分が「reason stack_trace_with_native_method」であり、最上位フレームがネイティブ・メソッドであるスタック・トレースが出力される場合、これはネイティブ・メソッドで割当て失敗が発生したことを示しています。これと前のメッセージとの違いは、割当ての失敗がJVMコードではなく、Java Native Interface (JNI)またはネイティブ・メソッドで検出されたことです。

処置: このタイプのOutOfMemoryError例外がスローされた場合は、OSのネイティブ・ユーティリティを使用して、問題をより詳しく診断する必要がある場合があります。「オペレーティング・システムのネイティブ・ツール」を参照してください。

OutOfMemoryErrorのかわりにクラッシュのトラブルシューティング

致命的エラー・ログやクラッシュ・ダンプの情報を使用して、クラッシュをトラブルシューティングします。

ネイティブ・ヒープからの割当てが失敗した直後に、アプリケーションがクラッシュすることがあります。これは、メモリー割当て関数によって返されたエラーをチェックしないネイティブ・コードで発生します。

たとえば、mallocシステム・コールは使用可能なメモリーがない場合にnullを返します。mallocからの戻り値がチェックされない場合、アプリケーションは無効なメモリー位置にアクセスしようとしてクラッシュする可能性があります。状況によっては、このタイプの問題を特定することが難しいことがあります。

しかし、致命的エラー・ログやクラッシュ・ダンプの情報があれば、この問題を十分に診断できる場合もあります。致命的エラー・ログについては、「致命的エラー・ログ」で詳しく説明されています。割当ての失敗によってクラッシュが生じた場合は、割当ての失敗の理由を調べてください。ネイティブ・ヒープに関する他の問題と同様に、システムに十分な容量のスワップ空間が構成されていなかったり、システム上の別のプロセスがすべてのメモリー・リソースを消費していたり、システムのメモリー不足を引き起こすリークがアプリケーション内(またはそこから呼び出されたAPI内)で発生していたりする可能性があります。

Java言語コードでのリークの診断

NetBeansプロファイラを使用してJava言語コードのリークを診断します。

Java言語コード内のリークの診断は難しい可能性があります。通常、アプリケーションの非常に詳しい知識が必要です。さらに、プロセスが反復し、冗長なことがよくあります。この項では、Java言語コード内のメモリー・リークを診断するために使用できるツールについて説明します。

注意:

この項で説明したツールに加えて、数多くのサードパーティ・メモリー・デバッガ・ツールが利用可能です。メモリー・デバッグ機能を備えた商用ツールの例として、Eclipse Memory Analyzer Tool (MAT)とYourKit (www.yourkit.com)の2つがあります。他にも数多くありますが、推奨される特定の製品はありません。

Java言語コードのリークの診断に使用されるユーティリティを次に示します。

1. NetBeansプロファイラ: NetBeansプロファイラは、メモリー・リークの場所をすばやく見つけることができます。商用のメモリー・リーク・デバッグ・ツールは、大規模なアプリケーション内のリークを特定するのに長い時間がかかる場合があります。しかし、NetBeans Profilerは、このようなオブジェクトが一般的に示すメモリーの割り当てと再利用のパターンを使用します。このプロセスには、メモリー再利用の欠落も含まれます。このプロファイラは、これらのオブジェクトがどこで割り当てられたかをチェックできます(リークの根本原因を特定するには、通常はこれで十分です)。

NetBeansプロファイラに関する項を参照してください。

次の項では、Java言語コードでのリークを診断するためのその他の方法について説明します。

- ヒープ・ヒストグラムの取得

- ファイナライズを保留中のオブジェクトのモニター

ヒープ・ヒストグラムの取得

メモリー・リークを特定するためのヒープ・ヒストグラムは、様々なコマンドやオプションを使用して取得できます。

ヒープ・ヒストグラムを調べることで、メモリー・リークをすばやく絞り込んでみることができます。ヒープ・ヒストグラムはいくつかの方法で取得できます。

- -XX:+PrintClassHistogramコマンド行オプションを指定してJavaプロセスを起動すると、Ctrl+Breakハンドラによってヒープ・ヒストグラムが生成されます。

- jmapユーティリティを使用して、実行中のプロセスからヒープ・ヒストグラムを取得できます。

診断機能を強化し、パフォーマンスのオーバーヘッドを削減するには、jmap,ユーティリティのかわりに最新のユーティリティjcmdの使用をお薦めします。「jcmdユーティリティの便利なコマンド」を参照してください。次の例のコマンドでは、jcmdを使用して実行中のプロセスのヒープ・ヒストグラムを作成し、jmapコマンドと同じような結果が得られます。

jcmd <process id/main class> GC.class_histogram filename=Myheaphistogram

jmap -histo pid

この出力には、ヒープ内の各クラス型の合計サイズとインスタンス数が表示されます。ヒストグラムを連続して(たとえば、2分ごとに)取得すると、詳細な分析につなげることができる傾向が見える場合があります。

- 次の例に示すように、jmapユーティリティを使用してコア・ファイルからヒープ・ヒストグラムを取得できます。

jmap -histo core_file

たとえば、アプリケーションの実行中に-XX:+HeapDumpOnOutOfMemoryErrorコマンド行オプションを指定すると、OutOfMemoryError例外がスローされたときに、JVMによってヒープ・ダンプが生成されます。その後、次の例に示すように、コア・ファイルに対してjmapを実行すると、ヒストグラムを取得できます。

$ jmap -histo \ /java/re/javase/6/latest/binaries/solaris-sparc/bin/java core.27421

Attaching to core core.27421 from executable
/java/re/javase/6/latest/binaries/solaris-sparc/bin/java, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 1.6.0-beta-b63
Iterating over heap. This may take a while...
Heap traversal took 8.902 seconds.

Object Histogram:

Size      Count   Class description
-------------------------------------------------------
86683872  3611828 java.lang.String
20979136  204     java.lang.Object[]
403728    4225    * ConstMethodKlass
306608    4225    * MethodKlass
220032    6094    * SymbolKlass
152960    294     * ConstantPoolKlass
108512    277     * ConstantPoolCacheKlass
104928    294     * InstanceKlassKlass
68024     362     byte[]
65600     559     char[]
31592     359     java.lang.Class
27176     462     java.lang.Object[]
25384     423     short[]
17192     307     int[]
:

前述の例は、java.lang.Stringオブジェクトの数(ヒープ内の3,611,828個のインスタンス)によってOutOfMemoryError例外が生じたことを示しています。文字列がどこで割り当てられたかは、詳細な分析を行わないとはっきりしません。それでもなおこの情報は有用です。

ファイナライズを保留中のオブジェクトのモニター

ファイナライズが保留されているオブジェクトをモニターするには、様々なコマンドやオプションを使用できます。

OutOfMemoryError例外が「Java heap space」という詳細メッセージとともにスローされた場合、その原因はファイナライザの過度の使用によるものである可能性があります。これを診断するために、ファイナライズを保留しているオブジェクトの数をモニターするためのオプションが複数用意されています。

- JConsole管理ツールを使用して、ファイナライズを保留中のオブジェクト数をモニターできます。このツールでは、「サマリー」タブ・ペインのメモリー統計にファイナライズ保留中の数が報告されます。この数は概算ですが、アプリケーションを特徴付け、アプリケーションがファイナライズに大きく依存しているかどうかを理解するために使用できます。

- Oracle SolarisおよびLinuxオペレーティング・システムでは、jmapユーティリティを-finalizerinfoオプションとともに使用することで、ファイナライズを待機しているオブジェクトに関する情報を出力できます。

- アプリケーションで、java.lang.management.MemoryMXBeanクラスのgetObjectPendingFinalizationCountメソッドを使用して、ファイナライズを保留しているオブジェクトのおよその数を報告できます。APIドキュメントへのリンクおよびコード例は、「カスタム診断ツール」に記載されています。コード例を拡張して、ファイナライズ保留中の数の報告を簡単に追加できます。

ネイティブ・コードでのリークの診断

複数の手法を使用してネイティブ・コードのメモリー・リークを検出し、切り分けることができます。一般に、すべてのプラットフォームに対応できる理想的な解決方法はありません。

ネイティブ・コードを診断するためのいくつかの手法を次に示します。

- すべてのメモリー割当ておよび解放呼出しの追跡

- JNIライブラリのすべてのメモリー割当ての追跡

- オペレーティング・システム・サポートによるメモリー割当ての追跡

- dbxデバッガによるリークの検出

- libumemツールによるリークの検出

すべてのメモリー割当ておよび解放呼出しの追跡

ツールを使用して、すべてのメモリー割当ておよびそのメモリーの使用方法を追跡できます。

非常によく使われる方法は、ネイティブ割当てのすべての割当て呼び出しと解放呼出しを追跡することです。これは、かなり簡単なプロセスになる場合と、非常に高度なプロセスになる場合があります。ネイティブ・ヒープの割り当てとそのメモリーの使用の追跡をめぐっては、長年にわたって多くの製品が構築されています。

IBM Rational PurifyのようなツールやSun Studioのdbxデバッガの実行時チェック機能を使用すると、ネイティブ・コードの通常の状況でこれらのリークを検出したり、初期化されていないメモリーへの割当てや解放されたメモリーへのアクセスを表すネイティブ・ヒープ・メモリーへのアクセスを検出したりできます。「dbxデバッガによるリークの検出」を参照してください。

これらのタイプのツールのすべてがネイティブ・コードを使用するJavaアプリケーションで機能するわけではなく、これらのツールは通常はプラットフォーム固有です。仮想マシンはコードを実行時に動的に作成するため、これらのツールはコードを誤って解釈したり、まったく動作しなかったり、誤った情報を与えたりする可能性があります。ツールのベンダーに問い合せて、ツールのバージョンが、使用している仮想マシンのバージョンで動作することを確認してください。

多数の簡単で移植可能なネイティブ・メモリー・リークの検出例は、sourceforgeを参照してください。ほとんどのライブラリやツールは、アプリケーションのソースを再コンパイルまたは編集して、割当て関数にラッパー関数を被せることができることを前提としています。より強力なツールでは、これらの割当て関数に動的に介入することで、アプリケーションを変更せずに実行できます。これは、Oracle Solaris 9オペレーティング・システムupdate 3で最初に導入されたlibumem.soライブラリのケースです(「libumemツールによるリークの検出」を参照)。

JNIライブラリのすべてのメモリー割当ての追跡

JNIライブラリを記述する場合は、簡単なラッパー・アプローチを使用して、ライブラリでメモリー・リークが発生しないことを保証する局所的な方法の作成を検討してください。

次の例の手順は、JNIライブラリ用の簡単な局所的割当て追跡方法です。まず、すべてのソース・ファイルに次の行を定義します。

#include <stdlib.h>
#define malloc(n) debug_malloc(n, __FILE__, __LINE__)
#define free(p) debug_free(p, __FILE__, __LINE__)

これにより、次の例の関数を使用してリークを監視できます。

/* Total bytes allocated */
static int total_allocated;
/* Memory alignment is important */
typedef union { double d; struct {size_t n; char *file; int line;} s; } Site;
void *
debug_malloc(size_t n, char *file, int line)
{
char *rp;
rp = (char*)malloc(sizeof(Site)+n);
total_allocated += n;
((Site*)rp)->s.n = n;
((Site*)rp)->s.file = file;
((Site*)rp)->s.line = line;
return (void*)(rp + sizeof(Site));
}
void
debug_free(void *p, char *file, int line)
{
char *rp;
rp = ((char*)p) - sizeof(Site);
total_allocated -= ((Site*)rp)->s.n;
free(rp);
}

その場合、JNIライブラリは定期的(またはシャットダウン時)にtotal_allocated変数の値をチェックして、それが妥当であることを確認する必要があります。前述のコードを拡張して、残された割当てをリンク・リストに保存し、リークしたメモリーがどこで割り当てられたかを報告することもできます。これは、単一セットのソース内のメモリー割り当てを追跡するための、局所的で移植可能な方法です。debug_free()がdebug_malloc()に由来するポインタのみを指定して呼び出されたことを確認する必要があり、realloc()、calloc()、strdup()などが使用されていた場合は、これらに対しても同じような関数を作成する必要があります。

より大域的な方法でネイティブ・ヒープのメモリー・リークを検出するには、プロセス全体のライブラリ呼び出しへの介入が必要になります。

オペレーティング・システム・サポートによるメモリー割当ての追跡

ツールを使用して、オペレーティング・システムのメモリー割当てを追跡できます。

ほとんどのオペレーティング・システムには、なんらかの形式の大域的割り当て追跡サポートが含まれています。

- Windowsでは、MSDNライブラリでデバッグ・サポートを検索してください。Microsoft C++コンパイラには、メモリー割当てを追跡するための追加サポートを自動的に取り込む、/Mdおよび/Mddコンパイラ・オプションがあります。

- Linuxシステムには、割り当て追跡を扱う際に役立つ、mtraceやlibnjamdなどのツールがあります。

- Oracle Solarisオペレーティング・システムには、watchmallocツールが用意されています。Oracle Solaris 9オペレーティング・システムUpdate 3では、libumemツールも提供されています。「libumemツールによるリークの検出」を参照してください。

dbxデバッガによるリークの検出

dbxデバッガには、リークを検出できる実行時チェック(RTC)機能が含まれています。dbxデバッガはOracle Solaris Studioの一部で、Linuxでも使用できます。

次の例はdbxセッションのサンプルです。

$ dbx ${java_home}/bin/java
Reading java
Reading ld.so.1
Reading libthread.so.1
Reading libdl.so.1
Reading libc.so.1
(dbx) dbxenv rtc_inherit on
(dbx) check -leaks
leaks checking - ON
(dbx) run HelloWorld
Running: java HelloWorld
(process id 15426)
Reading rtcapihook.so
Reading rtcaudit.so
Reading libmapmalloc.so.1
Reading libgen.so.1
Reading libm.so.2
Reading rtcboot.so
Reading librtc.so
RTC: Enabling Error Checking...
RTC: Running program...
dbx: process 15426 about to exec("/net/bonsai.sfbay/export/home2/user/ws/j2se/build/solaris-i586/bin/java")
dbx: program "/net/bonsai.sfbay/export/home2/user/ws/j2se/build/solaris-i586/bin/java"
just exec'ed
dbx: to go back to the original program use "debug $oprog"
RTC: Enabling Error Checking...
RTC: Running program...
t@1 (l@1) stopped in main at 0x0805136d
0x0805136d: main       :        pushl    %ebp
(dbx) when dlopen libjvm { suppress all in libjvm.so; }
(2) when dlopen libjvm { suppress all in libjvm.so; }
(dbx) when dlopen libjava { suppress all in libjava.so; }
(3) when dlopen libjava { suppress all in libjava.so; }
(dbx) cont
Reading libjvm.so
Reading libsocket.so.1
Reading libsched.so.1
Reading libCrun.so.1
Reading libm.so.1
Reading libnsl.so.1
Reading libmd5.so.1
Reading libmp.so.2
Reading libhpi.so
Reading libverify.so
Reading libjava.so
Reading libzip.so
Reading en_US.ISO8859-1.so.3
hello world
hello world
Checking for memory leaks...

Actual leaks report    (actual leaks:           27  total size:      46851 bytes)

Total     Num of  Leaked     Allocation call stack
Size      Blocks  Block
Address
==========  ====== =========== =======================================
44376       4      -      calloc < zcalloc
1072       1  0x8151c70  _nss_XbyY_buf_alloc < get_pwbuf < _getpwuid <
GetJavaProperties < Java_java_lang_System_initProperties <
0xa740a89a< 0xa7402a14< 0xa74001fc
814       1  0x8072518  MemAlloc < CreateExecutionEnvironment < main
280      10      -      operator new < Thread::Thread
102       1  0x8072498  _strdup < CreateExecutionEnvironment < main
56       1  0x81697f0  calloc < Java_java_util_zip_Inflater_init < 0xa740a89a<
0xa7402a6a< 0xa7402aeb< 0xa7402a14< 0xa7402a14< 0xa7402a14
41       1  0x8072bd8  main
30       1  0x8072c58  SetJavaCommandLineProp < main
16       1  0x806f180  _setlocale < GetJavaProperties <
Java_java_lang_System_initProperties < 0xa740a89a< 0xa7402a14<
0xa74001fc< JavaCalls::call_helper < os::os_exception_wrapper
12       1  0x806f2e8  operator new < instanceKlass::add_dependent_nmethod <
nmethod::new_nmethod < ciEnv::register_method <
Compile::Compile #Nvariant 1 < C2Compiler::compile_method <
CompileBroker::invoke_compiler_on_method <
CompileBroker::compiler_thread_loop
12       1  0x806ee60  CheckJvmType < CreateExecutionEnvironment < main
12       1  0x806ede8  MemAlloc < CreateExecutionEnvironment < main
12       1  0x806edc0  main
8       1  0x8071cb8  _strdup < ReadKnownVMs < CreateExecutionEnvironment < main
8       1  0x8071cf8  _strdup < ReadKnownVMs < CreateExecutionEnvironment < main

この出力は、プロセスが終了しようとしている時点でメモリーが解放されない場合、dbxデバッガがメモリー・リークを報告することを示しています。ただし、初期化時に割り当てられ、プロセスが終了するまで必要となるメモリーは、多くの場合、ネイティブ・コードでは解放されません。したがって、そのような場合は、dbxデバッガが実際にはリークではないメモリー・リークを報告することがあります。

注意:
前の例では、仮想マシンlibjvm.soとJavaサポート・ライブラリlibjava.soで報告されたリークを抑制するために、2つのsuppressコマンドを使用しています。

libumemツールによるリークの検出

libumem.soライブラリとモジュラ・デバッガmdbは、最初にOracle Solaris 9オペレーティング・システムupdate 3で導入され、メモリー・リークをデバッグするために使用できます。

libumemを使用する前に、次の例に示すように、libumemライブラリをプリロードし、環境変数を設定する必要があります。

$ LD_PRELOAD=libumem.so
$ export LD_PRELOAD
$ UMEM_DEBUG=default
$ export UMEM_DEBUG

ここで、Javaアプリケーションを実行しますが、それを終了する前に停止します。次の例では、_exitシステム・コールが呼び出されたときにtrussを使用してプロセスを停止しています。

$ truss -f -T _exit java MainClass arguments

この時点で、次の例に示すように、mdbデバッガを接続できます。

$ mdb -p pid
>::findleaks

::findleaksコマンドは、mdbでメモリー・リークを検出するコマンドです。リークが検出されたら、このコマンドによって割当て呼出しのアドレス、バッファ・アドレス、および最も近いシンボルを出力します。

bufctl構造体をダンプして、メモリー・リークが発生した割当てのスタック・トレースを取得することもできます。この構造体のアドレスは、::findleaksコマンドの出力から取得できます。

メモリー・リークの原因のトラブルシューティングについては、libumemを使用したメモリー・リークの分析を参照してください。

4 JFRを使用したパフォーマンスの問題のトラブルシューティング

この章では、Javaアプリケーションのパフォーマンスの問題を識別し、Javaフライト・レコーダを使用してこれらの問題をデバッグします。

Java Flight Recorder (JFR)は商用機能です。開発者のデスクトップまたはラップトップ上では無償で、また評価目的であればテスト環境、開発環境および本番環境で使用できます。ただし、JFRを本番サーバーで有効にする場合は、商用ライセンスが必要です。それ以外の目的でJMC UIをJDK上で使用する場合、商用ライセンスは必要ありません。

JFRの商用ライセンスの詳細は、ライセンス契約を参照してください。フライト記録の作成の詳細は、「フライト記録の作成方法」を参照してください。

Javaフライト・レコーダは、パフォーマンスの問題を調査するための優れたツールです。パフォーマンス・オーバーヘッドによって結果をゆがめることなく、これほど多くのプロファイリング・データを提供するツールは他にありません。この章では、識別できるパフォーマンスの問題の例を示し、Javaフライト・レコーダを使用して問題をデバッグします。

この章の構成は、次のとおりです。

- JFRのオーバーヘッド

- ボトルネックの検出

- ガベージ・コレクションのパフォーマンス

- 同期パフォーマンス

- I/Oパフォーマンス

- コード実行パフォーマンス

JFRのオーバーヘッド

パフォーマンスを測定する場合は、フライト・レコーダ自体によって追加されるパフォーマンス・オーバーヘッドを考慮することが重要です。オーバーヘッドは、アプリケーションに応じて異なります。パフォーマンス・テストがセット・アップされている場合は、特定のアプリケーションに顕著なオーバーヘッドが存在するかどうかを測定できます。

ただし、デフォルトの設定を使用して標準のプロファイリング記録を記録するためのオーバーヘッドは、ほとんどのアプリケーションで2%未満です。通常、標準の連続記録の実行では、測定可能なパフォーマンスへの影響はありません。

オーバーヘッドの1つの大きな要因は、デフォルトで無効になっているヒープ統計イベントです。ヒープ統計を有効にすると、テスト実行の開始時と終了時にOldガベージ・コレクションがトリガーされます。これらのOld GCは、アプリケーションに追加の休止時間を与えるので、遅延を測定している場合または休止時間の影響を受けやすい環境では、ヒープ統計を有効にして実行しないでください。メモリー・リークをデバッグするとき、またはアプリケーションのライブ・セットを調査するとき、ヒープ統計は便利です。「Javaフライト・レコーダを使用したメモリー・リークのデバッグ」を参照してください。

注意:
パフォーマンスのプロファイリングのユース・ケースでは、この情報は必要でない場合があります。

ボトルネックの検出

ボトルネックは、アプリケーションごとに異なります。一部のアプリケーションにとって、ボトルネックは、I/Oまたはネットワークの待機、スレッド間の同期、または実際のCPU使用率である場合があります。また、ガベージ・コレクションの回数がボトルネックである場合もあります。複数のボトルネックを持つアプリケーションもあります。

アプリケーションのボトルネックを見つけるための1つの方法として、「イベント」タブを調べます。これは詳細設定タブであり、実行することがいくつかあります。まず、「イベント」タブをクリックし、JFRウィンドウの左側にある「イベント・タイプ」タブを開きます。ここは、調査するイベントを選択する場所です。現時点では、図4-1に示すように、統計情報と割当てを除くすべてのJavaアプリケーションのイベントを選択します。

図4-1 ボトルネックの検出 - Javaアプリケーションのイベント

これで、すべての「イベント」タブに、これらのイベントのみが表示されるようになります。次に、図4-2に示すように、「グラフ」タブからJavaアプリケーションのメイン・スレッドを見てください。

図4-2 ボトルネックの検出 - 「グラフ」タブのメイン・スレッド

「グラフ」タブは、最初は把握するのが困難かもしれません。各行はスレッドであり、各スレッドは複数の行を持つことができます。図4-2の各スレッドは、この記録のために「イベント・タイプ」タブで有効にされたJavaアプリケーションのイベントを表す行を持ちます。選択されたJavaアプリケーションのイベントはすべて、スレッド・ストール・イベントであるという重要なプロパティを持ちます。スレッド・ストールは、スレッドがイベント中にアプリケーションを実行していなかったことを示し、これらはすべて期間イベントです。期間イベントは、アプリケーションが実行されていなかった時間を測定します。

「イベント・タイプ」タブから、各イベントの色を見てください。たとえば、黄色は、Java Monitor Waitイベントを表します。黄色い部分は、スレッドがオブジェクトを待機しているときです。これは多くの場合、スレッドがタスクを待機して、アイドル状態であることを意味します。赤は、Java Monitor Blockedイベントまたは同期イベントを表します。Javaアプリケーションの重要なスレッドがブロックされた状態で多くの時間を費やしている場合は、アプリケーションの重要なセクションがシングル・スレッドであり、それがボトルネックであることを意味しています。青は、ソケット読取りおよびソケット書込みイベントを表します。再び、Javaアプリケーションがソケットを待機している時間が多い場合、主なボトルネックは、ネットワーク内またはアプリケーションが通信する他のマシンにある可能性があります。

図4-2で、緑は、イベントがない部分を表します。緑の部分は、スレッドがスリープ中、待機中、ソケットの読取り中ではなく、ブロックもされていないことを意味します。通常、ここはアプリケーション・コードが実行される場所です。Javaアプリケーションの重要なスレッドがアプリケーション・イベントを生成せずに多くの時間を費やしている場合、アプリケーションのボトルネックは、コードの実行に費やした時間、またはCPU自体です。

注意:
ほとんどのJavaアプリケーション・イベント・タイプについて、20ミリ秒より長いイベントのみが記録されます。(このしきい値は、フライト記録を開始するときに変更できます。)要約すると、アプリケーションがファイルへの書込み(一度に一部分のみ)などの短いタスクを多数実行しているか、非常に短時間の同期に時間を費やしているので、領域には記録されたイベントがない可能性があります。

前述の各ボトルネックは、フライト記録内でさらに調査できます。

「イベント」タブには、ガベージ・コレクションが表示されず、ガベージ・コレクションがボトルネックになるかどうかを示しません。ガベージ・コレクションのパフォーマンスについて次のトピックを参照してください。

ガベージ・コレクションのパフォーマンス

Javaアプリケーションのガベージ・コレクションの問題は、JFRを使用して診断できます。

HotSpotガベージ・コレクタをチューニングすると、パフォーマンスに大きな影響を与える可能性があります。概要は、ガベージ・コレクション・チューニング・ガイドを参照してください。

まず、アプリケーションが起動し実行中であるとき、そのプロファイリング・フライト記録を取ります。ヒープ統計を含めると、追加のOldコレクションがトリガーされるので、ヒープ統計を含めないでください。よいサンプルを得るには、より長い記録を取ります(たとえば1時間)。

「メモリー」タブ、「GC回数」サブタブの順に選択します。「GC回数」は、GCの全体的なパフォーマンスへの影響を調査するために最適なタブです。右上の「すべてのコレクションの一時休止時間」セクションを参照し、記録からの「平均の一時休止合計」、「最大の一時休止合計」および「合計一時休止時間」を見てください。「休止の合計」は、GC中にアプリケーションが一時休止した時間の合計です。多くのGCは、バックグラウンドでほとんどの処理を行います。この場合、GCの長さは重要ではなく、アプリケーションが実際に停止していた時間が重要です。したがって、「休止の合計は、GCの影響を適切に測定します。

図4-3は、5分間のフライト記録を示しています(時間選択バーに表示されたとおり)。この間の「平均の一時休止合計」は16ms、「最大の一時休止合計」は49ms、および「合計一時休止時間」は2s 86msでした。

図4-3 ガベージ・コレクションのパフォーマンス - GCの一時休止

ガベージ・コレクションの主なパフォーマンスの問題は、通常、個々のGCの時間がかかりすぎるか、GCの一時休止(GCの一時休止の合計)に多くの時間を費やしているかのいずれかです。

個々のGCに時間がかかりすぎる場合は、GCの戦略を変更する必要があります。一時休止の回数とスループット・パフォーマンスの対比となると、各GCには異なるトレードオフがあります。動作ベースのチューニングに関する項を参照してください。

たとえば、ファイナライザまたは準参照の使用を減らすように、アプリケーションの修正が必要になる場合もあります。

アプリケーションが一時休止している時間が多すぎる場合は、様々な回避方法があります。

1つの方法は、Javaヒープ・サイズを増加することです。「ガベージ・コレクション」サブタブを見てアプリケーションが使用するヒープ・サイズを推定し、「Xms」および「Xmx」をより高い値に変更します。Javaヒープが大きいほど、GC間の時間は長くなります。Javaアプリケーションにメモリー・リークがあると、GCがますます頻繁になり、最後にはOutOfMemoryErrorがスローされる可能性があるので、注意が必要です。詳細は、「Javaフライト・レコーダによるメモリー・リークのデバッグ」を参照してください。

GCの数を減らすもう一つの方法は、より少ない一時オブジェクトを割り当てることです。「割当て」タブの下で、記録の過程で割り当てられているメモリ量を見てください。小さなオブジェクトは「TLAB」内に割り当てられ、大きなオブジェクトは「TLAB」外に割り当てられています。多くの場合、割当ての大半は「TLAB」内で起こります。

最後に、必要なGCを減らすために割当て率を減らします。「新しいTLABの割当て」タブ、「割当て」タブの順に選択して、メモリー不足が激しい割当てサイトおよびスタック・トレースを調べます。クラスごとにそれを表示するか、「スレッドによる割当て」を選択してほとんどの割当てを消費しているスレッドを確認できます。

JFRの「割当て」タブの一般的な詳細は、「フライト記録の検査」を参照してください。

これ以外のいくつかの設定により、JavaアプリケーションのGCのパフォーマンスが向上することもあります。GCパフォーマンスの詳細は、『Java Platform, Standard Edition HotSpot Virtual Machineガベージ・コレクション・チューニング・ガイド』のガベージ・コレクション・チューニング・ガイドに関する項を参照してください。

同期パフォーマンス

Javaアプリケーションの同期の問題をデバッグする場合、またはアプリケーションのスレッドがモニターに入るのを待機している時間が長い場合、「スレッド」タブ・グループの「競合」タブを見てください。

最も競合しているロック、およびロックの取得を待機しているスレッドのスタック・トレースに注目してください(図4-4を参照)。

図4-4 同期パフォーマンス - 「競合」タブ

図4-4では、上部の範囲セレクタを使用してイベントが発生した場所を確認できます。選択した時間範囲内の競合イベントの範囲セレクタを拡大してください。

通常、問題になると予想されていなかった競合を探します。ロギングは、一部のアプリケーションで予想外のボトルネックになる可能性がある共通領域です。

プログラムの更新後またはいずれかの特定の時間に、Javaアプリケーションのパフォーマンスの低下が見られる場合は、状況がよいときにフライト記録を取り、状況が悪いときに別のフライト記録を取って、大きく増加している同期サイトを探します。

注意:
範囲セレクタに表示されるイベントは、すべてが同期イベントとはかぎりません。デフォルトでは、期間が20ミリ秒より長い競合イベントが記録されます。(このしきい値は、フライト記録を開始するときに変更できます。)より短いしきい値は、より多くのイベントを与えますが、オーバーヘッドが増加する可能性があります。競合が問題であると考えている場合は、非常に低いしきい値(ほんの数ミリ秒)で短い記録を取ることができます。これをライブ・アプリケーションで実行する場合は、非常に短い記録から開始し、パフォーマンス・オーバーヘッドを監視してください。

I/Oパフォーマンス

アプリケーションのI/Oの問題は「I/O」グループの下の「ソケット読取り」タブをモニターすれば診断できます。

Javaアプリケーションがソケット読取り、ソケット書込み、ファイルの読込みまたはファイル書込みに多くの時間を費やしている場合は、I/Oまたはネットワークがボトルネックである可能性があります。アプリケーションのI/Oの問題を診断するには、図4-5に示すように、「I/O」グループの下の「ソケット読取り」タブを見ます。

図4-5 I/Oパフォーマンスの問題 - 「ソケット読取り」タブ

図4-5では、アプリケーションのリモート・アドレス198.51.100.0からの読取り数は100であると示されています。読み取られたバイト数の合計は356バイトで、待機時間の合計は1分57秒です。左上の「イベント別」タブを選択し、各イベントに費やされた時間と読み取られたデータを分析します。

ファイルまたはネットワークI/Oの問題も同様の方法で診断します。読取り/書込みの最も多いファイルを調べ、各ファイルの読取り/書込みとI/Oにかかった時間を確認します。

デフォルトでは、I/Oのすべてのタブは、期間が20ミリ秒より長いイベントを表示します。フライト記録を開始するとき、より多くのデータを収集するために、ファイルI/Oのしきい値またはソケットI/Oのしきい値を下げることができますが、パフォーマンスへの影響が大きくなる可能性があります。

コード実行パフォーマンス

コード実行パフォーマンスは、Java Mission Control、「呼出しツリー」タブを使用してモニターできます。

Javaアプリケーションのイベントが多くない場合、アプリケーションの主なボトルネックは、実行中のコードである可能性があります。まず、「スレッド」タブを見て、「概要」タブを選択します。CPU使用率の経時的推移を見ます。これは、記録されているJVMのCPU使用率とマシン上の総CPU使用率を示しています。JVMのCPU使用率は低いが、マシンのCPU使用率が高い場合は、他のアプリケーションが多くのCPUを消費していることを意味します。次に、「システム」タブ・グループの「プロセス」タブで、システム上で実行されている他のアプリケーションを見ます。ただし、それらのCPU使用率は表示されない場合があるので、多くのCPUを使用しているプロセスを調べるには、Topまたはタスク・マネージャなどのOSツールを使用する方が簡単です。

アプリケーションが多くのCPU時間を使用している場合は、「コード」タブ・グループを選択し、「ホット・スレッド」タブを見てください。このタブには、最も多くのCPU時間を使用するスレッドが表示されます。ただし、この情報はメソッド・サンプリングに基づいているので、サンプル数が少ない場合には100%正確でないことがあります。JFRが実行されているとき、JVMはスレッドをサンプリングします。デフォルトでは、連続記録ではいくつかのメソッド・サンプリングのみが実行され、プロファイリング記録ではできるかぎり多く実行されます。メソッド・サンプリングでは、コードを実行しているスレッドだけからデータを収集します。I/O待ち、スリープ中、ロック待ちなどのスレッドはサンプリングされません。したがって、メソッド・サンプルが多いスレッドは、CPU時間を最も多く使用しているスレッドですが、各スレッドがどのくらいのCPUを使用しているかはわかりません。

「コード」タブ・グループの「ホット・メソッド」タブは、アプリケーションが実行時間の大半を費やしている場所を見つけるのに役立ちます。このタブには、スタックの上位メソッドによりグループ化されたすべてのサンプルが表示されます。「呼出しツリー」タブを使用して、スタック・トレースの最下位のメソッドから始め、上方に移動します。図4-6では、Thread.runから始めて、最も多くサンプリングされている呼出しを見ています。

図4-6 コード実行パフォーマンス - 「呼出しツリー」タブ

第II部 JVMの問題のデバッグ

JVMの問題をデバッグするための様々なデバッグ手法。

この部では、次の問題の原因と様々なデバッグ方法について説明します。

- システム・クラッシュのトラブルシューティング
システム・クラッシュをトラブルシューティングする具体的な手順に関する指針を説明します。

- プロセスのハングおよびループのトラブルシューティング
ハングアップまたはループしているプロセスをトラブルシューティングする具体的な手順に関する指針を説明します。

- シグナルおよび例外の処理
Java HotSpot Server VMによるシグナル処理と例外処理に関する指針を説明します。

5 システム・クラッシュのトラブルシューティング

システム・クラッシュをトラブルシューティングする具体的ないくつかの手順に関する情報と指針。

クラッシュ(致命的エラー)が発生すると、プロセスが異常終了します。クラッシュにはさまざまな理由が考えられます。たとえば、Java HotSpot VM内、システム・ライブラリ内、Java SEのライブラリまたはAPI内、アプリケーション・ネイティブ・コード内、さらにはオペレーティング・システム(OS)内のバグによってクラッシュが発生する可能性があります。OSのリソース不足などの外部要因によってクラッシュが発生することもあります。

Java HotSpot VMまたはJava SEライブラリ・コードのバグによってクラッシュが発生することはほとんどありません。この章では、クラッシュを調査し、(可能な場合は)バグの原因が診断され修正されるまで問題のいくつかを回避する方法を提案します。

一般にどのようなクラッシュでも、最初の手順は致命的エラー・ログを見つけることです。これは、クラッシュの発生時にJava HotSpot VMによって生成されるテキスト・ファイルです。このファイルを見つける方法とファイルの詳細な説明については、「致命的エラー・ログ」を参照してください。

この章の構成は、次のとおりです。

- クラッシュの発生場所の特定

- 回避方法の特定

- Microsoft Visual C++のバージョンに関する考慮事項

クラッシュの発生場所の特定

エラー・ログを使用してクラッシュの原因を見つける方法を説明するいくつかの例、およびその原因に応じて問題をトラブルシューティングするためのいくつかのヒント。

エラー・ログのヘッダーは、エラーのタイプと問題のあるフレームを示し、スレッド・スタックは現在のスレッドおよびスタック・トレースを示しています。「ヘッダー形式」を参照してください。

クラッシュには次の原因が考えられます。

- ネイティブ・コードでのクラッシュ

- コンパイル済コードでのクラッシュ

- HotSpotコンパイラ・スレッドでのクラッシュ

- VMスレッドでのクラッシュ

- スタック・オーバーフローによるクラッシュ

ネイティブ・コードでのクラッシュ

クラッシュ・ダンプ・ファイルまたはコア・ファイルを分析して、ネイティブ・コードまたはJava Native Interface (JNI)ライブラリ・コードでクラッシュが発生したかどうかを識別します。

問題のあるフレームがネイティブ・ライブラリであることが致命的エラー・ログで示されている場合は、ネイティブ・コードまたはJava Native Interface (JNI)ライブラリ・コード内にバグが存在する可能性があります。クラッシュが他のなにかで発生した可能性もありますが、最初はライブラリとコア・ファイルまたはクラッシュ・ダンプを分析するのが適切です。致命的なエラー・ログのヘッダーからの次の例の抜粋を検討してください。

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  SIGSEGV (0xb) at pc=0x417789d7, pid=21139, tid=1024
#
# Java VM: Java HotSpot(TM) Server VM (6-beta2-b63 mixed mode)
# Problematic frame:
# C  [libApplication.so+0x9d7]

この場合は、ライブラリlibApplication.so内で実行されているスレッドでSIGSEGVが発生しました。

場合によっては、ネイティブ・ライブラリ内のバグがJava VMコード内のクラッシュとして表れます。_thread_in_vm状態で(つまり、Java VMコードで実行されているとき)、JavaThreadが失敗した次の例のクラッシュを検討してください。

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x08083d77, pid=3700, tid=2896
#
# Java VM: Java HotSpot(TM) Client VM (1.5-internal mixed mode)
# Problematic frame:
# V  [jvm.dll+0x83d77]

---------------  T H R E A D  ---------------

Current thread (0x00036960):  JavaThread "main" [_thread_in_vm, id=2896]
:
Stack: [0x00040000,0x00080000),  sp=0x0007f9f8,  free space=254k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [jvm.dll+0x83d77]
C  [App.dll+0x1047]          <========= C/native frame
j  Test.foo()V+0
j  Test.main([Ljava/lang/String;)V+0
v  ~StubRoutines::call_stub
V  [jvm.dll+0x80f13]
V  [jvm.dll+0xd3842]
V  [jvm.dll+0x80de4]
V  [jvm.dll+0x87cd2]
C  [java.exe+0x14c0]
C  [java.exe+0x64cd]
C  [kernel32.dll+0x214c7]
:

この場合、問題のあるフレームはVMフレームですが、スレッド・スタックは、App.dll内のネイティブ・ルーチンが(おそらくJNIを使って)VM内に呼び出されたことを示しています。

ネイティブ・ライブラリでのクラッシュを解決する最初の手順は、クラッシュが発生したネイティブ・ライブラリのソースを調べることです。

- ネイティブ・ライブラリがアプリケーションによって提供されている場合は、ネイティブ・ライブラリのソース・コードを調べます。-Xcheck:jniオプションをコマンド行に追加してアプリケーションを実行することで、JNIコードに関する多くの問題を特定できます。「-Xcheck:jniオプション」を参照してください。

- ネイティブ・ライブラリが別のベンダーから提供され、アプリケーションで使用されている場合は、このサードパーティ・アプリケーションに対するバグ・レポートを提出し、致命的エラー・ログの情報を提供します。

- クラッシュが発生したネイティブ・ライブラリがJava Runtime Environment (JRE)の一部(awt.dllやnet.dllなど)である場合は、ライブラリまたはAPIのバグが発生した可能性があります。その場合は、できるだけ多くのデータを収集し、ライブラリ名を示してバグまたはレポートを提出してください。JREライブラリは、JREディストリビューションのjre/libまたはjre/binディレクトリにあります。「バグ・レポートの提出」を参照してください。

ネイティブ・アプリケーション・ライブラリ内のクラッシュをトラブルシューティングするには、ネイティブ・デバッガをコア・ファイルまたはクラッシュ・ダンプ(利用可能な場合)に接続します。ネイティブ・デバッガは、OSに応じてdbx、gdb、またはwindbgです。「オペレーティング・システムのネイティブ・ツール」を参照してください。

コンパイル済コードでのクラッシュ

致命的エラー・ログを分析して、コンパイル済コードでクラッシュが発生したかどうかを識別します。

コンパイル済コードでクラッシュが発生したことが致命的エラー・ログで示されている場合は、不正なコードを生成するコンパイラ・バグが発生した可能性があります。問題のあるフレームのタイプがJ (コンパイル済みのJavaフレームを意味する)である場合は、コンパイル済みコードでクラッシュが発生したと認識できます。次に示すのはこのようなクラッシュの例です。

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  SIGSEGV (0xb) at pc=0x0000002a99eb0c10, pid=6106, tid=278546
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (1.6.0-beta-b51 mixed mode)
# Problematic frame:
# J  org.foobar.Scanner.body()V
#
:
Stack: [0x0000002aea560000,0x0000002aea660000),  sp=0x0000002aea65ddf0,
free space=1015k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
J  org.foobar.Scanner.body()V

[error occurred during error reporting, step 120, id 0xb]

注意:
完全なスレッド・スタックは入手できません。「error occurred during error reporting」(エラー報告中にエラーが発生しました)という出力行は、スタック・トレースを取得しようとして問題が発生したこと(スタックの破損を示している可能性がある)を意味します。

コンパイラを切り替えるか、クラッシュを引き起こしたメソッドをコンパイルから除外することによって、問題を一時的に回避できる可能性があります。

「HotSpotコンパイラ・スレッドまたはコンパイル済コードでのクラッシュの回避」を参照してください。

HotSpotコンパイラ・スレッドでのクラッシュ

致命的エラー・ログを分析して、HotSpotコンパイラ・スレッドでクラッシュが発生したかどうかを識別します。

致命的エラー・ログの出力に、現在のスレッドがCompilerThread0、CompilerThread1、またはAdapterCompilerという名前のJavaThreadであることが示されている場合は、コンパイラのバグが発生した可能性があります。この場合は、コンパイラを切り替える(たとえば、HotSpot Server VMのかわりにHotSpot Client VMを使用する、またはその逆)か、クラッシュを引き起こしたメソッドをコンパイルから除外することによって、問題を一時的に回避しなければいけないことがあります。

「HotSpotコンパイラ・スレッドまたはコンパイル済コードでのクラッシュの回避」を参照してください。

VMスレッドでのクラッシュ

致命的エラー・ログを分析して、VMThreadでクラッシュが発生したかどうかを識別します。

致命的エラー・ログの出力に、現在のスレッドがVMThreadであることが示されている場合は、THREADセクションでVM_Operationを含む行を探してください。VMThreadは、HotSpot VMの特別なスレッドです。VM内でガベージ・コレクション(GC)などの特殊なタスクを実行します。VM_Operationによって、操作がGCであることが示唆された場合は、ヒープの破損などの問題が発生した可能性があります。

GCの問題以外にも、ヒープ内のオブジェクト参照を不整合または不正な状態にしておく他の何か(コンパイラやランタイムのバグなど)が問題である可能性もあります。この場合は、環境に関する情報をできるだけ多く収集し、可能な回避策を試してください。問題がGCに関連する場合は、GCの構成を変更することによって問題を一時的に回避できることがあります。

「ガベージ・コレクション中のクラッシュの回避」を参照してください。

スタック・オーバーフローによるクラッシュ

Java言語コード内でスタック・オーバーフローが発生すると、通常は違反したスレッドがjava.lang.StackOverflowError例外をスローします。

一方、CおよびC++はスタックの終わりを超えて書込みを行い、スタック・オーバーフローを起こします。これは、プロセスが終了する致命的エラーです。

HotSpot実装では、Javaメソッドがスタック・フレームをC/C++ネイティブ・コード(つまり、ユーザー・ネイティブ・コードおよび仮想マシン自体)と共有します。Javaメソッドは、スタック空間を超えることなくネイティブ・コードを呼び出せるように、スタックの終わりに向かって一定距離のスタック空間が使用可能であるかどうかをチェックするコードを生成します。スタックの終わりまでの距離をシャドウ・ページと呼びます。シャドウ・ページのサイズは、プラットフォームに応じて3-20ページです。この距離は調整可能であるため、デフォルトより長い距離を必要とするネイティブ・コードを含むアプリケーションは、シャドウ・ページのサイズを増やすことができます。シャドウ・ページを増やすためのオプションは-XX:StackShadowPages=nですnはプラットフォームのデフォルトのシャドウ・ページより大きくします。

アプリケーションでセグメンテーション・フォルトが発生してもコア・ファイルや致命的エラー・ログがない場合は、「致命的エラー・ログ」を参照してください。または、Windows上でSTACK_OVERFLOW_ERRORが発生したか、または「An irrecoverable stack overflow has occurred」(回復不能なスタック・オーバーフローが発生しました)というメッセージが表示された場合、これはStackShadowPagesの値を超過したため、より多くの空間が必要であることを示しています。

StackShadowPagesの値を増やす場合は、-Xssパラメータを使用してデフォルトのスレッド・スタック・サイズも増やす必要があることがあります。デフォルトのスレッド・スタック・サイズを増やすと、作成できるスレッドの数が減る可能性があるため、スレッド・スタック・サイズの値は慎重に選択してください。スレッド・スタック・サイズは、プラットフォームによって256 KBから1024 KBまで様々です。

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x10001011, pid=296, tid=2940
#
# Java VM: Java HotSpot(TM) Client VM (1.6-internal mixed mode, sharing)
# Problematic frame:
# C  [App.dll+0x1011]
#

---------------  T H R E A D  ---------------

Current thread (0x000367c0):  JavaThread "main" [_thread_in_native, id=2940]
:
Stack: [0x00040000,0x00080000),  sp=0x00041000,  free space=4k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [App.dll+0x1011]
C  [App.dll+0x1020]
C  [App.dll+0x1020]
:
C  [App.dll+0x1020]
C  [App.dll+0x1020]
...<more frames>...

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  Test.foo()V+0
j  Test.main([Ljava/lang/String;)V+0
v  ~StubRoutines::call_stub

前述の例から次の情報を解釈できます。

- 例外はEXCEPTION_STACK_OVERFLOWです。

- スレッドの状態は_thread_in_nativeで、スレッドがネイティブ・コードまたはJNIコードを実行していることを意味します。

- スタック情報では、空き領域は4 KB (Windowsシステムの単一ページ)のみです。さらに、スタック・ポインタ(sp)はスタックの終わり(0x00040000)に近い0x00041000です。

- ネイティブ・フレームの出力は、再帰的なネイティブ関数がこの場合の問題であることを示しています。出力の...という表記は、追加のフレームが存在するが、出力されなかったことを示します。出力は100フレームまでに制限されています。

回避方法の特定

重要なアプリケーションでクラッシュが発生した場合に使用可能な回避策。

重要なアプリケーションでクラッシュが発生し、HotSpot VM内のバグによってクラッシュが発生したように見える場合は、一時的な回避策をすばやく見つけることが望ましい場合があります。JDKの最新リリースとともに配備されたアプリケーションでクラッシュが発生した場合は、Oracleにクラッシュを報告してください。

重要:

このセクションの回避方法によってクラッシュが正常に除去されても、その回避方法は問題の修正ではなく、一時的な解決方法でしかありません。問題が見つかった元の構成で、サポートに電話するか、バグ・レポートを提出してください。

システム・クラッシュの回避方法を見つけるための3つのシナリオを次に示します。

- HotSpotコンパイラ・スレッドまたはコンパイル済みコードでのクラッシュの回避

- ガベージ・コレクション中のクラッシュの回避

- クラス・データ共有によって発生するクラッシュの回避

HotSpotコンパイラ・スレッドまたはコンパイル済みコードでのクラッシュの回避

HotSpotコンパイラ・スレッドでクラッシュが発生した場合に使用可能な回避策。

コンパイラ・スレッドでクラッシュが発生したことが致命的エラー・ログに示された場合は、コンパイラのバグが発生した可能性があります(ただし、常にそうであるとはかぎりません)。同様に、コンパイルされたコードでクラッシュが発生した場合は、コンパイラが不正なコードを生成した可能性があります。

HotSpot Client VM (-clientオプション)の場合は、コンパイラ・スレッドがCompilerThread0としてエラー・ログに表示されます。HotSpot Server VMの場合は、複数のコンパイラ・スレッドがあり、それらがCompilerThread0、CompilerThread1、およびAdapterThreadとしてエラー・ログ・ファイルに表示されます。

JDK 7u5のリリース以降、HotSpotコンパイラはデフォルトで無視されます。コマンドライン・オプションを使用して以前の動作をシミュレートすることが可能であり、これは複数のメソッドが除外されている場合に便利です。JDK 7u5の重要なバグ修正に関する項を参照してください。

.hotspot_compileファイルのかわりにJVMフラグを使用してメソッドをコンパイルから除外するには、Java Platform, Standard Editionツール・リファレンスの拡張JITコンパイラ・オプションの-XX:CompileCommandに関する項を参照してください。

次の例は、開発中に発生し、修正されたコンパイラ・バグのエラー・ログのフラグメントを示しています。このログ・ファイルは、HotSpot Server VMが使用され、CompilerThread1でクラッシュが発生したことを示しています。このログ・ファイルはさらに、現在のCompileTaskがjava.lang.Thread.setPriorityメソッドのコンパイルだったことを示しています。

# An unexpected error has been detected by HotSpot Virtual Machine:
#
:
# Java VM: Java HotSpot(TM) Server VM (1.5-internal-debug mixed mode)
:
---------------  T H R E A D  ---------------

Current thread (0x001e9350): JavaThread "CompilerThread1" daemon [_thread_in_vm, id=20]

Stack: [0xb2500000,0xb2580000),  sp=0xb257e500,  free space=505k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0xc3b13c]
:

Current CompileTask:
opto: 11      java.lang.Thread.setPriority(I)V (53 bytes)

---------------  P R O C E S S  ---------------

Java Threads: ( => current thread )
0x00229930 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=21]
=>0x001e9350 JavaThread "CompilerThread1" daemon [_thread_in_vm, id=20]
:

この場合は、可能な回避策が2つあります。

- 強引な方法: HotSpot Client VMを指定するため、アプリケーションが-clientオプションで実行されるように構成を変更します。

- 丁寧な方法: java.lang.Thread.setPriorityメソッドのコンパイル中にのみバグが発生するものと想定して、このメソッドをコンパイルから除外します。

最初の(-clientオプションを使用する)方法は、一部の環境では容易に構成できる可能性があります。それ以外で、構成が複雑な場合や、VMを構成するコマンド行に容易にアクセスできない場合は、より困難になる可能性があります。一般に、HotSpot Server VMからHotSpot Client VMに切り替えることで、アプリケーションのピーク・パフォーマンスも低下します。環境によっては、問題の診断と修正が完了するまで、これを容認できる場合があります。

2つ目の(メソッドをコンパイルから除外する)方法では、アプリケーションの作業ディレクトリ内に.hotspot_compilerファイルを作成する必要があります。次の例に、このアプローチを示します。

exclude java/lang/Thread setPriority

一般に、このファイルの形式はexcludeclassmethodです。ここで、classは(パッケージ名で完全修飾された)クラス、methodはメソッドの名前です。コンストラクタ・メソッドは<init>として指定され、staticイニシャライザは<clinit>として指定されます。

注意:

.hotspot_compilerファイルは、サポートされていないインタフェースです。ここでは、トラブルシューティングと一時的な回避策の特定のみを目的として記載しています。

アプリケーションが再起動された後、コンパイラは.hotspot_compilerファイル内の除外されたメソッドをコンパイルしようとしません。場合によっては、クラッシュの根本原因を診断してバグを修正するまで、これが一時的な救済策になる可能性があります。

HotSpot VMが、2番目のアプローチの前述の例に示した.hotspot_compilerファイルを正しく検出して処理したことを確認するには、実行時に次のログ情報を探してください。

注意:
ファイル名のセパレータはスラッシュではなくドットです。

ガベージ・コレクション中のクラッシュの回避

ガベージ・コレクション時にクラッシュが発生した場合に使用可能な回避策。

ガベージ・コレクション(GC)中にクラッシュが発生した場合は、致命的エラー・ログに、VM_Operationが進行中であることが報告されます。ここでは説明のため、ほぼ同時に処理されるGC (-XX:+UseConcMarkSweep)は使用されないことを前提としてください。ログのTHREADセクションにはVM_Operationが表示され、次のいずれかの状況を示します。

- 割当て用の世代コレクション

- 全世代のコレクション

- パラレルGCの割当ての失敗

- パラレルGCの永続的な割当ての失敗

- パラレルGCのシステムGC

ほとんどの場合、ログで報告される現在のスレッドはVMThreadです。これは、HotSpot VMで特殊なタスクを実行するために使用される特別なスレッドです。次の例は、シリアル・ガベージ・コレクタでのクラッシュからの致命的エラー・ログのフラグメントです。

---------------  T H R E A D  ---------------

Current thread (0x002cb720):  VMThread [id=3252]

siginfo: ExceptionCode=0xc0000005, reading address 0x00000000

Registers:
EAX=0x0000000a, EBX=0x00000001, ECX=0x00289530, EDX=0x00000000
ESP=0x02aefc2c, EBP=0x02aefc44, ESI=0x00289530, EDI=0x00289530
EIP=0x0806d17a, EFLAGS=0x00010246

Top of Stack: (sp=0x02aefc2c)
0x02aefc2c:   00289530 081641e8 00000001 0806e4b8
0x02aefc3c:   00000001 00000000 02aefc9c 0806e4c5
0x02aefc4c:   081641e8 081641c8 00000001 00289530
0x02aefc5c:   00000000 00000000 00000001 00000001
0x02aefc6c:   00000000 00000000 00000000 08072a9e
0x02aefc7c:   00000000 00000000 00000000 00035378
0x02aefc8c:   00035378 00280d88 00280d88 147fee00
0x02aefc9c:   02aefce8 0806e0f5 00000001 00289530
Instructions: (pc=0x0806d17a)
0x0806d16a:   15 08 83 3d c0 be 15 08 05 53 56 57 8b f1 75 0f
0x0806d17a:   0f be 05 00 00 00 00 83 c0 05 a3 c0 be 15 08 8b

Stack: [0x02ab0000,0x02af0000),  sp=0x02aefc2c,  free space=255k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [jvm.dll+0x6d17a]
V  [jvm.dll+0x6e4c5]
V  [jvm.dll+0x6e0f5]
V  [jvm.dll+0x71771]
V  [jvm.dll+0xfd1d3]
V  [jvm.dll+0x6cd99]
V  [jvm.dll+0x504bf]
V  [jvm.dll+0x6cf4b]
V  [jvm.dll+0x1175d5]
V  [jvm.dll+0x1170a0]
V  [jvm.dll+0x11728f]
V  [jvm.dll+0x116fd5]
C  [MSVCRT.dll+0x27fb8]
C  [kernel32.dll+0x1d33b]

VM_Operation (0x0373f71c): generation collection for allocation, mode:
safepoint, requested by thread 0x02db7108

注意:

ガベージ・コレクション中のクラッシュがガベージ・コレクション実装内のバグを示すとはかぎりません。コンパイラやランタイムのバグ、またはその他の問題を示す場合もあります。

ガベージ・コレクション中にクラッシュが繰返し発生する場合は、次の回避策を試すことができます。

- GCの構成を切り替えます。たとえば、シリアル・コレクタを使用している場合はスループット・コレクタを試します(またはその逆)。

- HotSpot Server VMを使用している場合は、HotSpot Client VMを試します。

使用されているガベージ・コレクタがわからない場合は、Oracle SolarisおよびLinuxオペレーティング・システム上でjmapユーティリティを使用できます。コア・ファイルが使用可能な場合は、コア・ファイルからヒープ情報を取得するために「jmapユーティリティ」を参照してください。一般に、GC構成がコマンド行で指定されていない場合、Windowsではシリアル・コレクタが使用されます。Oracle SolarisおよびLinuxオペレーティング・システムではマシンの構成に依存します。マシンに少なくとも2 GBのメモリーと少なくとも2基のCPUが搭載されている場合は、スループット・コレクタ(パラレルGC)が使用されます。小規模なマシンでは、シリアル・コレクタがデフォルトです。シリアル・コレクタを選択するためのオプションは-XX:+UseSerialGCで、スループット・コレクタを選択するためのオプションは-XX:+UseParallelGCです。回避策としてスループット・コレクタからシリアル・コレクタに切り替えた場合、マルチプロセッサ・システムではパフォーマンスがある程度低下する可能性があります。根本的な問題の診断と修正が完了するまで、これを容認できる場合があります。

クラス・データ共有によって発生するクラッシュの回避

JREをインストールすると、インストーラがシステムJARファイルから一連のクラスをprivate内部表現にロードして、その表現を「共有アーカイブ」と呼ばれるファイルにダンプします。JVMを開始すると、共有アーカイブがメモリー・マッピングされ、これらのクラス用の読取り専用のJVMメタデータを複数のJVM処理で共有できます。共有アーカイブの復元はクラスのロードより速いため、起動時間が短縮され、コストを節約できます。クラス・データ共有は、Java HotSpot VMでサポートされます。G1、シリアル、パラレルおよびparallelOldGCガベージ・コレクタがサポートされています。共有文字列機能(クラス・データ共有の一部)では、Windows以外のプラットフォーム上のG1ガベージ・コレクタのみがサポートされています。

致命的エラー・ログでは、ログのヘッダーにバージョン文字列が出力されます。共有が有効になっている場合は、次の例に示すように「sharing」というテキストでそれが示されます。

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x08083d77, pid=3572, tid=784
#
# Java VM: Java HotSpot(TM) Client VM (1.5-internal mixed mode, sharing)
# Problematic frame:
# V  [jvm.dll+0x83d77]

CDSを無効にするには、コマンド行に-Xshare:offオプションを指定します。共有が有効になっているときにのみクラッシュが発生する場合は、この機能にバグが発生した可能性があります。その場合は、できるだけ多くの情報を収集し、バグ・レポートを提出してください。

Microsoft Visual C++のバージョンに関する考慮事項

JDKソフトウェアは、Microsoft Visual Studio 2013を使用してWindows上に構築されています。

Javaアプリケーションでクラッシュが発生し、コンパイラの異なるリリースでコンパイルされたネイティブ・ライブラリまたはJNIライブラリがある場合は、ランタイム間の互換性の問題を考慮する必要があります。具体的には、Microsoftのガイドラインに従って複数のランタイムを処理している場合にのみ、環境がサポートされます。たとえば、あるランタイムを使用してメモリーを割り当てた場合は、同じランタイムを使用してそれを解放する必要があります。リソースの割当て時と異なるライブラリを使用してリソースを解放すると、予測できない動作やクラッシュが発生する可能性があります。

6 プロセスのハングおよびループのトラブルシューティング

この章では、ハング・アップまたはループしているプロセスをトラブルシューティングする具体的な手順に関する情報と指針を説明します。

ハング・アップまたはループしているプロセスが関与する問題が発生することがあります。ハング・アップは様々な理由で発生する可能性がありますが、多くの場合、アプリケーション・コード、APIコード、またはライブラリ・コード内のデッドロックが原因です。Java HotSpot VMのバグが原因でハングアップが発生することもあります。

ハング・アップのように見えて、実はループであることが判明する場合もあります。たとえば、VMプロセス内のバグによって1つ以上のスレッドが無限ループに入ると、使用可能なCPUサイクルがすべて消費される可能性があります。

ハングアップを診断する最初の手順は、VMプロセスがアイドル状態なのか、それとも使用可能なCPUサイクルをすべて消費しているかを調べることです。これはオペレーティング・システム(OS)のネイティブ・ユーティリティを使用して行うことができます。プロセスがビジー状態で、使用可能なCPUサイクルをすべて消費しているように見える場合は、デッドロックではなくループしているスレッドが問題である可能性が高いです。たとえばOracle Solarisオペレーティング・システムでは、prstat -L -p pidコマンドを使用してターゲット・プロセスのすべての軽量プロセス(LWP)の統計を表示して、CPUサイクルを大量に消費しているスレッドを特定できます。

この章の構成は、次のとおりです。

- ループ処理の診断

- ハング・プロセスの診断

- Oracle Solaris 8スレッド・ライブラリ

ループ処理の診断

VMプロセスがループしているように見える場合、最初の手順はスレッド・ダンプを取得しようとすることです。スレッド・ダンプを取得できれば、多くの場合、どのスレッドがループしているかがはっきりします。ループしているスレッドを識別できたら、スレッド・ダンプ内のトレース・スタックから、そのスレッドがループしている場所(および場合によっては理由)に関する手掛かりを得ることができます。

アプリケーション・コンソール(標準入力/出力)を使用できる場合は、[Ctrl]+[\]キーの組合せ(Oracle SolarisまたはLinuxの場合)または[Ctrl]+[Break]キーの組合せ(Windowsの場合)を押して、HotSpot VMにスレッドの状態を含むスレッド・ダンプを出力させることができます。Oracle SolarisおよびLinuxオペレーティング・システムでは、(kill -QUIT pidコマンドで)SIGQUITをプロセスに送信して、スレッド・ダンプを取得することもできます。この場合、スレッド・ダンプはターゲット・プロセスの標準出力に出力されます。プロセスの起動方法によっては、出力をファイルに送信することもできます。

-XX:+PrintClassHistogramコマンド行オプションを指定してJavaプロセスを起動すると、Ctrl+Breakハンドラによってヒープ・ヒストグラムが生成されます。

スレッド・ダンプを取得できる場合は、RUNNABLE状態のスレッドのスレッド・スタックから始めるのが適切です。スレッド・ダンプの形式、およびスレッド・ダンプ内で見られるスレッド状態の表の詳細は、「スレッド・ダンプ」を参照してください。場合によっては、どのスレッドが継続的にビジーに見えるかを特定するため、スレッド・ダンプを連続して取得する必要がある場合があります。

アプリケーション・コンソールを使用できない(プロセスがバックグラウンドで実行されていたり、VMの出力が未知の場所に送信されていたりするなど)場合は、jstackユーティリティを使用してスタック・スレッドを取得できます。このユーティリティの出力の詳細は、「jstackユーティリティ」を参照してください。Javaスレッドがループしている証拠がスレッド・ダンプに示されない場合も、jstackユーティリティを使用するようにしてください。

jstackユーティリティの出力を確認するときは、まずRUNNABLE状態のスレッドに注目してください。これは、ビジー状態でループしていると思われるスレッドが取る可能性がもっとも高い状態です。場合によっては、どのスレッドがループしているかを見極めるために、jstackを数回にわたって実行する必要があります。スレッドが常にRUNNABLE状態であるように見える場合は、-mオプションを使用してネイティブ・フレームを出力し、スレッドが実行している処理について詳細なヒントを得ることができます。スレッドがRUNNABLE状態のままで継続的にループしているように見える場合、この状況はより詳しい調査を必要とする潜在的HotSpot VMバグを示している可能性があります。

VMが[Ctrl]+[\]に応答しない場合、これはアプリケーション・コードやライブラリ・コードの問題ではなく、VMのバグを示している可能性があります。この場合は、-mオプションを指定したjstackを使用して、すべてのスレッドのスレッド・スタックを取得します。この出力には、VM内部のスレッドのスレッド・スタックが含まれます。このスタック・トレースで、待機しているように見えないスレッドを識別します。たとえばOracle Solarisオペレーティング・システムでは、__lwp_cond_wait、__lwp_park、___pollsysなどの関数、または他のブロック関数に入っていないスレッドを識別します。VMのバグによってループが発生しているように見える場合は、できるだけ多くのデータを収集して、バグ・レポートを提出してください。データ収集の詳細は、「バグ・レポートの提出」を参照してください。

ハング・プロセスの診断

スレッド・ダンプを使用して、ハングアップしたプロセスを診断します。

アプリケーションがハング・アップし、プロセスがアイドル状態になっているように見える場合は、最初の手順はスレッド・ダンプを取得してみることです。アプリケーション・コンソールを使用できる場合は、[Ctrl]+[\]キー(Oracle SolarisまたはLinuxの場合)または[Ctrl]+[Break]キー(Windowsの場合)を押して、HotSpot VMにスレッド・ダンプを出力させることができます。Oracle SolarisおよびLinuxオペレーティング・システムでは、(kill -QUIT pidコマンドで)SIGQUITをプロセスに送信して、スレッド・ダンプを取得することもできます。ハング・アップしたプロセスがスレッド・ダンプを生成できる場合、その出力はターゲット・プロセスの標準出力に出力されます。

スレッド・ダンプの出力後、HotSpot VMはデッドロック検出アルゴリズムを実行します。

次の項では、ハング・プロセスの様々な状況を説明します。

- デッドロックが検出された

- デッドロックが検出されなかった

- スレッド・ダンプがない

デッドロックが検出された

デッドロックが検出された場合は、デッドロックに関与したスレッドのスタック・トレースとともにそれが出力されます。

次の例は、この状況のスタック・トレースを示しています。

Found one Java-level deadlock:
=============================
"AWT-EventQueue-0":
waiting to lock monitor 0x000ffbf8 (object 0xf0c30560, a java.awt.Component$AWTTreeLock),
which is held by "main"
"main":
waiting to lock monitor 0x000ffe38 (object 0xf0c41ec8, a java.util.Vector),
which is held by "AWT-EventQueue-0"

Java stack information for the threads listed above:
===================================================
"AWT-EventQueue-0":
at java.awt.Container.removeNotify(Container.java:2503)
- waiting to lock <0xf0c30560> (a java.awt.Component$AWTTreeLock)
at java.awt.Window$1DisposeAction.run(Window.java:604)
at java.awt.Window.doDispose(Window.java:617)
at java.awt.Dialog.doDispose(Dialog.java:625)
at java.awt.Window.dispose(Window.java:574)
at java.awt.Window.disposeImpl(Window.java:584)
at java.awt.Window$1DisposeAction.run(Window.java:598)
- locked <0xf0c41ec8> (a java.util.Vector)
at java.awt.Window.doDispose(Window.java:617)
at java.awt.Window.dispose(Window.java:574)
at javax.swing.SwingUtilities$SharedOwnerFrame.dispose(SwingUtilities.java:1743)
at javax.swing.SwingUtilities$SharedOwnerFrame.windowClosed(SwingUtilities.java:1722)
at java.awt.Window.processWindowEvent(Window.java:1173)
at javax.swing.JDialog.processWindowEvent(JDialog.java:407)
at java.awt.Window.processEvent(Window.java:1128)
at java.awt.Component.dispatchEventImpl(Component.java:3922)
at java.awt.Container.dispatchEventImpl(Container.java:2009)
at java.awt.Window.dispatchEventImpl(Window.java:1746)
at java.awt.Component.dispatchEvent(Component.java:3770)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:463)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:214)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)
"main":
at java.awt.Window.getOwnedWindows(Window.java:844)
- waiting to lock <0xf0c41ec8> (a java.util.Vector)
at javax.swing.SwingUtilities$SharedOwnerFrame.installListeners(SwingUtilities.java:1697)
at javax.swing.SwingUtilities$SharedOwnerFrame.addNotify(SwingUtilities.java:1690)
at java.awt.Dialog.addNotify(Dialog.java:370)
- locked <0xf0c30560> (a java.awt.Component$AWTTreeLock)
at java.awt.Dialog.conditionalShow(Dialog.java:441)
- locked <0xf0c30560> (a java.awt.Component$AWTTreeLock)
at java.awt.Dialog.show(Dialog.java:499)
at java.awt.Component.show(Component.java:1287)
at java.awt.Component.setVisible(Component.java:1242)
at test01.main(test01.java:10)

Found 1 deadlock.

デフォルトのデッドロック検出は、synchronizedキーワードを使用して取得されたロック、およびjava.util.concurrentパッケージを使用して取得されたロックとともに動作します。Java VMのフラグ-XX:+PrintConcurrentLocksを設定すると、スタック・トレースにロックの所有者のリストも表示されます。

デッドロックが検出された場合は、デッドロックを理解するために出力を詳細に調べる必要があります。前の例では、スレッドmainがオブジェクト0xf0c30560をロックし、スレッドAWT-EventQueue-0によってロックされている0xf0c41ec8に入るのを待機しています。しかし、スレッドAWT-EventQueue-0はmainによってロックされている0xf0c30560に入るのを待機しています。

スタック・トレースの詳細から、デッドロックを見つけるのに役立つ情報が得られます。

デッドロックが検出されなかった

スレッド・ダンプが出力されたが、デッドロックが見つからなかった場合、この問題は通知されないモニターをスレッドが待機しているというバグである可能性があります。これは、タイミングの問題か、一般的なロジックのバグである可能性があります。

この問題についてさらに調べるには、スレッド・ダンプ内の各スレッドと、Object.wait()でブロックされた各スレッドを調べます。スタック・トレース内の呼出し側フレームは、wait()メソッドを呼び出しているクラスとメソッドを示しています。行番号情報付きでコードがコンパイルされている場合(デフォルト)は、これによって調査する必要があるコードに関する手掛かりが得られます。ほとんどの場合、この問題をより詳しく診断するには、アプリケーション・ロジックまたはライブラリに関してある程度の知識が必要です。一般に、アプリケーションでの同期動作、およびモニターがいつどこで通知されるかに関する詳細と条件について理解している必要があります。

スレッド・ダンプがない

VMでデッドロックが発生したかハングした場合は、jstackコマンドを使用します。

VMが[Ctrl]+[\]または[Ctrl]+[Break]に応答しない場合は、他のなんらかの理由でVMがデッドロックまたはハングアップした可能性があります。その場合は、jstackユーティリティを使用してスレッド・ダンプを取得します。これは、アプリケーションにアクセスできない場合や、出力が未知の場所に送信されている場合にも当てはまります。

jstackの出力で、BLOCKED状態になっている各スレッドを調べます。場合によっては、最上位フレームにスレッドがブロックされている理由(たとえば、Object.waitやThread.sleep)が示されていることがあります。スタックの残りの部分から、スレッドが実行している処理についてのヒントが得られます。これは、行番号情報付きでソースがコンパイルされている場合(デフォルト)に特に当てはまり、その場合はソース・コードを相互参照できます。

スレッドがBLOCKED状態になっていて、理由がはっきりしない場合は、-mオプションを使用して混合したスタックを取得します。混合したスタックの出力を使用して、スレッドがブロックされた理由を特定できるはずです。同期化メソッドまたはブロックに入ろうとしてスレッドがブロックされた場合は、スタックの最上位付近にObjectMonitor::enterのようなフレームが表示されます。混合スタックの出力例を次に示します。

----------------- t@13 -----------------
0xff31e8b8      ___lwp_cond_wait + 0x4
0xfea8c810      void ObjectMonitor::EnterI(Thread*) + 0x2b8
0xfeac86b8      void ObjectMonitor::enter2(Thread*) + 0x250
:

RUNNABLE状態のスレッドもブロックされる可能性があります。混合したスタックの最上位フレームに、スレッドが実行している処理が示されているはずです。

チェックすべき特定スレッドの1つはVMThreadです。これは、ガベージ・コレクション(GC)のような操作を実行するために使用される特殊なスレッドです。その初期のフレームでVMThread::run()を実行しているスレッドとして識別できます。Oracle Solarisでは通常t@4です。Linuxでは、C++分解名_ZN8VMThread4loopEvを使用して識別できるはずです。

一般にVMスレッドは、VM操作の実行を待機している、VM操作に備えてすべてのスレッドを同期している、またはVM操作を実行している、の3つの状態のいずれかを取ります。ハング・アップがアプリケーションやクラス・ライブラリのデッドロックではなく、HotSpot VMのバグであると疑われる場合は、VMスレッドに特別な注意を払ってください。

VMスレッドがSafepointSynchronize::beginでスタックしているように見える場合、これはVMがセーフポイントに移行する問題を示している可能性があります。セーフポイントは、VM内で実行されているすべてのスレッドがブロックされ、GCなどの特殊な操作が完了するのを待機していることを示します。

VMスレッドがfunctionでスタックしているように見えるが、functionがdoitで終了する場合、これはVMに問題があることを示している可能性もあります。

一般に、コマンド行からアプリケーションを実行できるが、VMが[Ctrl]+[\]または[Ctrl]+[Break]に応答しない状態になる場合は、VMのバグ、スレッド・ライブラリの問題、または別のライブラリのバグを発見した可能性が高くなります。これが発生した場合は、クラッシュ・ダンプを取得してください。できるだけ多くの情報を収集する手順について「コア・ダンプの収集」を参照して、バグ・レポートを提出するか、サポート部門に連絡してください。

ハングアップしたプロセスとの関連で説明する必要があるもう1つのツールは、Oracle Solarisオペレーティング・システムのpstackユーティリティです。Oracle Solaris 8および9オペレーティング・システムでは、このユーティリティはターゲット・プロセス内のLWPのスレッド・スタックを出力します。Oracle Solaris 10オペレーティング・システムおよびJDK 5.0リリース以降では、pstackの出力はjstack -mの出力に似ています(ただし、同じではありません)。jstackと同様に、Oracle Solaris 10オペレーティング・システムのpstackの実装では、完全修飾クラス名、メソッド名およびバイトコード・インデックス(BCI)が出力されます。また、行番号情報付きでソースがコンパイルされた場合(デフォルト)は、行番号も出力します。これは、/procファイル・システムの機能を実行するOracle Solarisオペレーティング・システム上の他のユーティリティに習熟している開発者や管理者にとって便利です。

Linuxには、pstackと同等のツールとしてlsstackがあります。このユーティリティは、一部のディストリビューションに含まれており、そうでない場合はsourceforgeから取得します。これを執筆している時点では、lsstackはネイティブ・フレームのみを報告していました。

Oracle Solaris 8スレッド・ライブラリ

Oracle Solaris 8オペレーティング・システム上のデフォルトのスレッド・ライブラリは多くの場合、T1ライブラリと呼ばれます。このスレッド・ライブラリはm:nスレッド・モデルを実装しました(m個のユーザー・スレッドがn個のカーネル・レベル・スレッド(LWP)にマップされます)。Oracle Solaris 8オペレーティング・システムには、代替の新しいスレッド・ライブラリも付属しており、/usr/lib/lwpにあります。代替スレッド・ライブラリは多くの場合、T2ライブラリと呼ばれ、Oracle Solaris 9および10オペレーティング・システムではデフォルトのスレッド・ライブラリになりました。古いリリース(特に1.4.0より前)のJ2SEでは、デフォルトのスレッド・ライブラリに関する問題(スレッド・ライブラリ内のバグ、LWPの同期化問題、LWPの枯渇など)がいくつかありました。LWPの枯渇は、RUNNABLE状態のユーザー・スレッドはあるが、使用可能なカーネル・レベル・スレッドがないというシナリオです。

前述の問題は過去のものですが、Oracle Solaris 8オペレーティング・システム上にJDKソフトウェアを配備すると、今でもデフォルトでT1ライブラリが使用されることに注意してください。JDKリリースでは「バインドされたスレッド」が使用され、ユーザー・スレッドがそれぞれ1つのカーネル・スレッドにバインドされるため、LWP枯渇タイプの問題は発生しません。しかし、スレッド・ライブラリの問題だと思われる問題(ハングアップなど)が発生した場合は、LD_LIBRARY_PATHに/usr/lib/lwpを追加することで、T2ライブラリを使用するようにHotSpot VMに指示できます。T2ライブラリが使用されているかどうかをチェックするには、pldd pidコマンドを発行して、指定したプロセスによってロードされるライブラリをリストしてください。

7 シグナルおよび例外の処理

この章では、Java HotSpot仮想マシンによるシグナルおよび例外の処理方法について説明します。また、独自のシグナル・ハンドラをインストールする必要があるアプリケーションを容易に作成できるように、Oracle Solaris、LinuxおよびmacOSオペレーティング・システムで使用可能なシグナル・チェーン機能についても説明します。

この章の構成は、次のとおりです。

- Oracle Solaris、LinuxおよびmacOSでのシグナルの処理

- Windowsでの例外処理

- シグナル・チェーン

- Java HotSpot VMによる例外処理

- コンソール・ハンドラ

- Oracle Solaris、LinuxおよびmacOSで使用されるシグナル

Oracle Solaris、LinuxおよびmacOSでのシグナルの処理

Java HotSpot VMは、様々な機能を実装して致命的エラー状態に対処するために、シグナル・ハンドラをインストールします。

たとえば、まれにjava.lang.NullPointerExceptionがスローされた場合に明示的なNULLチェックを回避する最適化では、SIGSEGVシグナルをキャッチして処理し、NullPointerExceptionをスローします。

一般に、シグナルやトラップが発生する場合には2つのカテゴリがあります。

- 暗黙的なNULL処理のような、シグナルが予想され処理される場合。もう1つの例として、セーフポイントが必要になったときにメモリー内のページを保護する、セーフポイント・ポーリング・メカニズムがあります。そのページにアクセスするスレッドによってSIGSEGVが生成され、その結果、スレッドをセーフポイントに移行させるスタブが実行されます。

- 予期しないシグナル。これには、VMコード、Java Native Interface (JNI)コード、またはネイティブ・コードで実行されたときのSIGSEGVが含まれます。これらの場合、このシグナルは予想されていないため、エラー・ログを作成してプロセスを終了するために致命的エラー処理が呼び出されます。

表7-2に、Oracle Solaris、LinuxおよびmacOSオペレーティング・システムで現在使用されているシグナルの一覧を示します。

Windowsでの例外処理

Windowsでは、例外はプログラムの実行中に発生するイベントです。

例外には、ハードウェア例外とソフトウェア例外の2種類があります。ハードウェア例外は、Oracle SolarisおよびLinuxオペレーティング・システム上のSIGSEGVやSIGKILLなどのシグナルに相当します。ソフトウェア例外は、アプリケーションまたはオペレーティング・システムがRaiseException() APIを使用して明示的に発行します。

Windowsでは、ハードウェア例外とソフトウェア例外の両方を処理するメカニズムを構造化例外処理(SEH)と呼びます。これは、C++やJavaの例外処理メカニズムに似たスタック・フレームベースの例外処理です。C++では、次の例に示すように__tryおよび__exceptキーワードを使用して、例外が発生する可能性があるコードのセクションを保護します。

__try {
// guarded body of code
} __except (filter-expression) {
// exception-handler block
}

__exceptブロックは、GetExceptionCode() APIから返される整数例外コード、またはGetExceptionInformation() APIから返される例外情報、あるいはその両方を使用するフィルタ式によってフィルタ処理されます。

このフィルタ式は、次のいずれかの値として評価されるはずです。

- EXCEPTION_CONTINUE_EXECUTION = -1
このフィルタ式によって状況が修復され、例外が発生した場所から実行が継続されます。一部の例外スキームと異なり、SEHは再開モデルもサポートします。これは、シグナル・ハンドラの完了後にプログラムが中断した場所から実行が継続するという点で、Unixのシグナル処理とよく似ています。違いは、この場合のハンドラがフィルタ式だけであり、__exceptブロックではないことです。ただし、フィルタ式に関数呼出しを含めることもできます。

- EXCEPTION_CONTINUE_SEARCH = 0
現在のハンドラはこの例外を処理できません。次のハンドラのハンドラ検索が続行されます。これは、C++およびJavaで例外型を照合しないcatchブロックに似ています。

- EXCEPTION_EXECUTE_HANDLER = 1
現在のハンドラはこの例外を照合し、処理できます。__exceptブロックが実行されます。

終了ハンドラを構築するには、次の例に示すように、__tryおよび__finallyキーワードを使用します。

__try {
// guarded body of code
} __finally {
// __finally block
}

(例外の後、または例外なしで) __tryブロックから制御が離れたときは、__finallyブロックが実行されます。__finallyブロックの内部でAbnormalTermination() APIを呼び出すと、例外のあとで制御が継続したかどうかをテストできます。

Windowsプログラムは、最上位の未処理例外フィルタ関数をインストールして、__try/__exceptブロックで処理されない例外をキャッチすることもできます。この関数は、SetUnhandledExceptionFilter() APIを使用してプロセス単位でインストールされます。ある例外のハンドラが存在しない場合は、UnhandledExceptionFilter()が呼び出され、これが最上位の未処理例外フィルタ関数を(あれば)呼び出して、その例外をキャッチします。この関数によって、未処理例外をユーザーに通知するメッセージ・ボックスも表示されます。

Windowsの例外は、現在の実行ストリームに起因するUnixの同期シグナルに相当します。Windowsでは、コンソール・イベント(たとえば、ユーザーがコンソールで[Ctrl]+[C]を押した場合)などの非同期イベントは、SetConsoleCtlHandler() APIを使用して登録されたコンソール制御ハンドラによって処理されます。

Windows上でアプリケーションがsignal() APIを使用すると、C実行時ライブラリ(CRT)はWindowsの例外とコンソール・イベントの両方を適切なシグナルまたはC実行時エラーにマップします。たとえば、CRTは[Ctrl]+[C]をSIGINTにマップし、他のすべてのコンソール・イベントをSIGBREAKにマップします。同様に、SIGSEGVハンドラを登録すると、CRTは対応する例外をシグナルに変換します。CRTの起動コードは、main()関数の周囲に__try/__exceptブロックを実装しています。CRTの例外フィルタ関数(名前は_XcptFilter)は、Win32例外をシグナルにマップし、シグナルを適切なハンドラにディスパッチします。シグナルのハンドラをSIG_DFL (デフォルトの処理)に設定すると、_XcptFilterはUnhandledExceptionFilterを呼び出します。

ベクトル化例外処理メカニズムを使用することもできます。ベクトル化ハンドラは、フレームベースのハンドラではありません。プログラムは、AddVectoredExceptionHandler APIを使用して0個以上のベクトル化例外ハンドラを登録できます。ベクトル化ハンドラは、構造化例外ハンドラ(あれば)の前に呼び出され、例外の発生場所に関係なく呼び出されます。

ベクトル化例外ハンドラは、次のいずれかの値を返します。

- EXCEPTION_CONTINUE_EXECUTION: 次のベクトル化およびSEHハンドラをスキップします。

- EXCEPTION_CONTINUE_SEARCH: 次のベクトル化またはSEHハンドラを続行します。

Windowsの例外処理の詳細は、MicrosoftのWebサイトを参照してください。

シグナル・チェーン

シグナル・チェーンでは、独自のシグナル・ハンドラをインストールする必要があるアプリケーションを書けます。この機能はSolaris、LinuxおよびmacOSで使用できます。

シグナル・チェーン機能は、次の機能を提供します。

- OracleのHotSpot仮想マシンの作成時に事前インストールされるシグナル・ハンドラに対するサポート。
HotSpot VMが作成されるときに、HotSpot VMで使用されるシグナルのシグナル・ハンドラが保存されます。実行中にこれらのシグナルが生成され、その対象がHotSpot VMでない場合は、事前インストールされたハンドラが呼び出されます。つまり、これらのシグナルのために、事前インストールされたシグナル・ハンドラはHotSpot VMハンドラの背後でチェーンされます。

- Java Native Interfaceコード内または別のネイティブ・スレッドからのどちらかで、HotSpot VMを作成した後にインストールされたシグナル・ハンドラのサポート。
アプリケーションは、libc/libthread/libpthreadライブラリの前にlibjsig.so共有ライブラリをリンクしてロードできます。HotSpot VMによってすでにインストールされているシグナル・ハンドラとハンドラが競合する場合に、このライブラリでは、signal()、sigset()、sigaction()などの呼出しがインターセプトされて、HotSpot VMで使用されるシグナル・ハンドラが置き換えれません。かわりに、これらのコールでは、新しいシグナル・ハンドラが保存されます。新しいシグナル・ハンドラは、これらのシグナルのために、HotSpot VM シグナル・ハンドラの背後でチェーンされます。実行中にこれらのシグナルが生成され、その対象がHotSpot VMでない場合は、事前インストールされたハンドラが呼び出されます。

VMの作成後にインストールされるシグナル・ハンドラに対するサポートが必要ない場合、libjsig.so共有ライブラリは不要です。

シグナル・チェーンを有効にするには、次の手順のいずれかを実行してlibjsig.so共有ライブラリを使用します。

- HotSpot VMを作成または埋め込むアプリケーションにlibjsig.so共有ライブラリをリンクします。

cc -L libjvm.so-directory -ljsig -ljvm java_application.c

- LD_PRELOAD環境変数を使用します。

- Kornシェル(ksh):

export LD_PRELOAD=libjvm.so-directory/libjsig.so; java_application

- Cシェル(csh):

setenv LD_PRELOAD libjvm.so-directory/libjsig.so; java_application

この間に呼び出されるsignal()、sigset()、およびsigaction()コールは、HotSpot VMによってインストールされ、オペレーティング・システムで認識されているシグナル・ハンドラではなく、保存されたシグナル・ハンドラを返します。

注意:

SIGQUIT、SIGTERM、SIGINT、およびSIGHUPシグナルはチェーンできません。アプリケーションでこれらのシグナルを処理する必要がある場合は、—Xrsオプションの使用を検討してください。

macOSのシグナル・チェーンの有効化

macOSでシグナル・チェーンを有効にするには、次の環境変数を設定します。

- DYLD_INSERT_LIBRARIES: SolarisおよびLinuxで使用可能なLD_PRELOAD環境変数のかわりに、指定されたライブラリがプリロードされます。

- DYLD_FORCE_FLAT_NAMESPACE: macOSには(シンボルの完全修飾名にそのライブラリが含まれる)2レベル・ネームスペースがあるため、libjsigライブラリの関数を有効にしてOSの実装を置き換えます。この機能を有効にするには、この環境変数を任意の値に設定します。

次のコマンドは、libjsigライブラリをプリロードして、シグナル・チェーンを有効にします。

$ DYLD_FORCE_FLAT_NAMESPACE=0 DYLD_INSERT_LIBRARIES="JAVA_HOME/lib/libjsig.dylib" java MySpiffyJavaApp

注意:
macOSのライブラリ・ファイル名はlibjsig.dylibで、SolarisまたはLinuxの場合のlibjsig.soではありません。

Java HotSpot VMによる例外処理

HotSpot VMでは、64ビット・システム用のAddVectoredExceptionHandlerAPIを使用して、初期化中に最上位の例外ハンドラをインストールします。

また、作成される各スレッドのスレッド(内部)起動関数呼出しの周囲に、C++の__try /__exceptブロックを使用してWin32 SEHをインストールします。

最後に、JNI関数の周囲に例外ハンドラをインストールします。

アプリケーションでJNIコード内の構造化例外を処理する必要がある場合は、C++の__try /__except文を使用できます。ただし、JNIコード内でベクトル化例外ハンドラを使用する必要がある場合、そのハンドラはVMの例外ハンドラに進むためにEXCEPTION_CONTINUE_SEARCHを返す必要があります。

一般に、例外が発生する2つのカテゴリがあります。

- 例外が予想され、処理されるとき。例として、前述の暗黙的なNULL処理があります(以前はNULLへのアクセスにはEXCEPTION_ACCESS_VIOLATIONを使用して処理されていました)。

- 予想外の例外。例として、VMコード、JNIコード、またはネイティブ・コード内で実行されているときのEXCEPTION_ACCESS_VIOLATIONがあります。これらの場合、このシグナルは予想されていないため、エラー・ログを作成してプロセスを終了するために致命的エラー処理が呼び出されます。

コンソール・ハンドラ

このトピックでは、Java HotSpot VMに登録されているコンソール・イベントのリストについて説明します。

Java HotSpot VMは、表7-1に示すコンソール・イベントを登録します。

表7-1 コンソール・イベント

コンソール・イベント

シグナル

使用方法

CTRL_C_EVENT

SIGINT

このイベントとシグナルは、プロセスを終了するために使用されます。(省略可能)

CTRL_CLOSE_EVENTCTRL_LOGOFF_EVENTCTRL_SHUTDOWN_EVENT

SIGTERM

このイベントとシグナルは、VMが異常終了したときにシャットダウン・フック・メカニズムによって使用されます。(省略可能)

CTRL_BREAK_EVENT

SIGBREAK

このイベントとシグナルは、Javaスタック・トレースを標準エラー・ストリームに出力するために使用されます。(省略可能)

アプリケーションが独自のコンソール・ハンドラを登録する必要がある場合は、-Xrsオプションを使用できます。このオプションを使用すると、(前述のイベントのマッピングによる) SIGTERMに対してシャットダウン・フックが実行されず、(前述の[Ctrl]+[Break]イベントのマッピングによる) SIGBREAKに対してスレッド・ダンプのサポートを利用できなくなります。

Oracle Solaris、LinuxおよびmacOSで使用されるシグナル

このトピックでは、Solaris OS、LinuxおよびmacOSで使用されるシグナルのリストについて説明します

表7-2 Oracle Solaris、LinuxおよびmacOSで使用されるシグナル

シグナル

説明

SIGSEGV、SIGBUS、SIGFPE、SIGPIPE、SIGILL

これらのシグナルは暗黙的なNULLチェックの実装などに使用されます。

SIGQUIT

このシグナルは、Javaスタック・トレースを標準エラー・ストリームに出力するために使用されます。(省略可能)

SIGTERM、SIGINT、SIGHUP

これらのシグナルは、VMが異常終了したときにシャットダウン・フック・メカニズム(java.lang.Runtime.addShutdownHook)をサポートするために使用されます。(省略可能)

SIGJVM1、SIGJVM2

これらのシグナルはJava仮想マシンで使用するために予約されています。(Solarisのみ)

SIGUSR2

このシグナルはLinuxおよびmacOSで内部的に使用されます。Solaris上のVMでは使用されません。

SIGABRT

HotSpot VMはこのシグナルを処理しません。かわりに、致命的エラー処理の後でabort関数を呼び出します。アプリケーションでこのシグナルを使用する場合は、期待されるセマンティックスを保持するため、プロセスを終了するようにしてください。

シグナルの使用を減らすために-Xrsオプションを指定すると、「オプション」としてタグ付けされたシグナルは使用されません。このオプションを使用すると、使用されるシグナルの数が減りますが、VMはSIGSEGVなどの基本的なシグナルのために独自のシグナル・ハンドラをインストールします。このオプションを指定すると、プロセスがSIGQUIT、SIGTERM、SIGINT、またはSIGHUPを受信した場合にシャットダウン・フック・メカニズムが実行されなくなります。VMが正常終了した場合(つまり、最後の非デーモン・スレッドが完了するか、System.exitメソッドが呼び出されると)、シャットダウン・フックが予想どおり実行されます。

SIGUSR2は、suspendとresumeをLinuxおよびmacOSで実装するために使用されます。ただし、SIGUSR2のかわりに代替シグナルを使用するように指定することも可能です。これを行うには、_JAVA_SR_SIGNUM環境変数を指定します。この環境変数を設定する場合は、SIGSEGVおよびSIGBUSの最大より大きい値に設定する必要があります。

第III部 コア・ライブラリの問題のデバッグ

この部では、タイムゾーン設定に関する問題とトラブルシューティング方法について説明します。内容は次のとおりです。

- JREでのタイムゾーン設定
Java Runtime Environment (JRE)でのタイムゾーン設定に関するいくつかの問題と、その問題を解決するためのトラブルシューティング方法について説明します。

8 JREでのタイムゾーン設定

この章では、Windowsオペレーティング・システムでJava Runtime Environment (JRE)のタイムゾーン設定に関連して発生する可能性のあるいくつかの問題について説明します。さらに、これらの問題を解決するためのトラブルシューティング方法および回避方法についても説明します。

この章の構成は、次のとおりです。

- ネイティブ・タイムゾーン情報およびJRE

- Windowsでのデフォルト・タイムゾーンの判別

ネイティブ・タイムゾーン情報およびJRE

JREでは、デフォルト・タイムゾーンを判別するためにネイティブ・タイムゾーン情報を読み取ります。

たとえば、Windowsでは、JREはデフォルト・タイムゾーンを判別するためにレジストリを問い合わせます。

しかし、JREでは独自のタイムゾーン・データベースも保持しています。これにはクロスプラットフォーム・サポートが備わっていますが、その理由は様々なオペレーティング・システムのAPIがJava APIをサポートするのに不十分であるからです。Javaタイムゾーン・データベースは、タイムゾーンIDをサポートし、JREがサポートしているすべてのタイムゾーンの夏時間ルールを決めます。tzupdaterツールは、Java SEダウンロード・ページからダウンロードして使用できます。

オペレーティング・システムがシステム時間をJREに配信できるように、JREへの変更をオペレーティング・システムごとに行う必要があります。その後、Javaアプリケーションが日付と時間に関連したコンストラクタを呼び出してシステム日付を要求すると、システム時間が返されます。

そのようなコンストラクタの例を次に示します。

- java.util.Date()

- java.util.GregorianCalendar()

日付と時間に関連したコンストラクタには、次が含まれています。

- System.currentTimeMillis()

- System.nanoTime()

正しいシステム時間が必ずJREに配信されるようにするには、オペレーティング・システム固有のパッチが必要になる場合があります。

次の項では、タイムゾーンの設定のトラブルシューティング手法について説明します。

- 使用中のタイムゾーン・データ・バージョンの判別

- TZupdaterの問題のトラブルシューティング

使用中のタイムゾーン・データ・バージョンの判別

Oracle提供のJREに付属しているタイムゾーン・データベースのバージョンは、リリース・ノートに記載されています。ただし、tzupdaterツールを使用してJREにパッチを適用した場合は、実際のバージョンがそこに記載されているバージョンと異なることがあります。

使用しているJREの現在のタイムゾーン・データ・バージョンを判別するには、Versionオプションを指定してtzupdaterツールを実行するか、ZoneInfoMappingsファイルのヘッダーを調べます。

タイムゾーン・データを確認するには、これらの2つの手順に従います。

- TZupdaterによるタイムゾーンの確認: Javaのタイムゾーン更新ツールはtzupdaterと呼ばれています。使用しているJREのタイムゾーン・データベース・バージョンを判別するには、このツールを次のように実行します。

java -jar tzupdater.jar -V

tzupdaterツールの実行による標準的な出力を次に示します。

tzupdater version 2.1.0-b04
JRE tzdata version: tzdata2016f

tzupdaterツールは、Timezone Updater ToolというWebページからダウンロードできます。

- ZoneInfoMappingsファイルの確認: tzupdaterツールがない場合でも、<java-home>/lib/zi/ZoneInfoMappingsファイルのヘッダーを調べることでバージョンをすぐに確認できます。このデータは、Javaに固有のバイナリ・フォーマットで保存されます。Oracle Solaris、LinuxおよびmacOSオペレーティング・システムでは、8進数のダンプ・コマンドを使用してこのファイルのヘッダーを調べることができます。
次の例に、8進数ダンプ・コマンド形式を示します。

/usr/bin/od -c -j 11 -N 11 <java-home>/lib/zi/ZoneInfoMappings

次に、dumpコマンドの一般的な結果を示します。

/usr/bin/od -c -j 11 -N 11 /farfaraway/jdks/jdk1.6.0_21/jre/lib/zi/ZoneInfoMappings0000000   t   z   d   a   t   a   2   0   1   0   i0000013

次の例は、このJREに埋め込まれているタイムゾーン・データのバージョンがtzdata2010iであることを示しています。

Microsoft Windowsでは、findstrコマンドを使用してZoneInfoMappingsファイルを調べることができます。次に例を示します。

findstr tzdata <java-home>\lib\zi\ZoneInfoMappings

Tzupdaterの問題のトラブルシューティング

tzupdaterの実行時に、それが「このJavaランタイムで使用できるタイムゾーン・データはありません」というメッセージを表示して終了することがときどきあります。次に2つの例を示します。

$ java -jar tzupdater.jar -V
tzupdater version 2.1.1-b01
JRE tzdata version: tzdata2017b
There's no tzdata available for this Java runtime.

考えられる原因は、Oracle提供でないJREを使用していることです。Oracleが提供するJREは、Oracle Solaris (x86、x64、SPARC)、Linux (x86、x64、ARM)、Microsoft Windows (x86、x64)、およびmacOS (x64)用です。これらのJREのjava.vendorプロパティ値はSun Microsystems Inc.、Oracle Corporation、またはBEA Systems, Inc.です。Oracleでは、それ以外のプラットフォーム用のJREを提供していません。

java -versionコマンドを実行した場合の出力は、JREの実際のベンダーを判断するのに十分な情報を提供しません。しかし、-vオプションを指定してtzupdaterを更新モードで実行すると、java.vendorプロパティが出力されます。次の例に、環境がHewlett PackardのHP_UXである場合のtzupdaterの実行結果を示します。

root@my_server:/opt/java6/bin> uname -a
HP-UX my_server B.11.23 U ia64 1114591084 unlimited-user license
root@my_server:/opt/java6/bin> ./java -version
java version "1.6.0.05"
Java(TM) SE Runtime Environment (build 1.6.0.05-jinteg_14_oct_2009_01_44-b00)
Java HotSpot(TM) Server VM (build 14.2-b01-jre1.6.0.05-rc5, mixed mode)
root@my_server:/opt/java6/bin> ./java -jar tzupdater.jar -v -l
java.home: /opt/java6/jre
java.vendor: Hewlett-Packard Co.
java.version: 1.6.0.05
JRE tzdata version: tzdata2009i
There's no tzdata available for this Java runtime.

前の例では、java.vendorが「Hewlett-Packard Co.」に設定されています。tzupdaterを使用して更新を試みているJREはOracleでサポートされていません。

考えられる解決方法は、使用しているJREベンダーのWebサイトを参照して、使用可能なタイムゾーン更新ツールがあるかどうかを確認することです。たとえば、Hewlett-Packardバージョンのtzupdaterツールを参照してください。

Windowsでのデフォルト・タイムゾーンの判別

このセクションでは、Windows VistaおよびWindows 7オペレーティング・システムでJREがデフォルト・タイムゾーンを判別する方法について明確に説明します。Javaが期待されたタイムゾーンを報告しない場合は、次の各セクションで説明するトラブルシューティング方法を使用してください。

- JREが報告するデフォルト・タイムゾーンの確認

- コントロール・パネルでの設定の確認

- 夏時間の自動調整の確認

- コントロール・パネルでのデフォルト・タイムゾーンの設定

- -Duser.timezoneシステム・プロパティの確認

- Windows 7の特別なツール

- タイムゾーン・マッピングのJRE内部表現

JREが報告するデフォルト・タイムゾーンの確認

ネイティブ・オペレーティング・システムによるチェックに基づいて、JREがデフォルト・タイムゾーンとして報告するタイムゾーンを確認するための簡単なプログラムを作成できます。

次の例のJavaプログラムはデフォルト・タイムゾーンを返します。

public class DefaultTimeZone {
public static void main(String[] args) {
System.out.println(java.util.TimeZone.getDefault().getID());
}
}

前の例のコード・スニペットをDefaultTimeZone.javaという名前のファイルに保存し、javacコマンドを使用してコンパイルできます。次に、次の例に示すように、コンパイルしたDefaultTimeZoneクラスを実行できます。

c:\tztest> javac DefaultTimeZone.java
c:\tztest> java DefaultTimeZone
Europe/Berlin

前の例では、デフォルト・タイムゾーンはEurope/Berlinです。プログラムを実行すると、ローカル・タイムゾーンが表示されるはずです。出力が予期されたタイムゾーンでない場合は、次のトラブルシューティング手順を続けて実行してください。

コントロール・パネルでの設定の確認

Windowsコントロール・パネルを使用して、システムのデフォルト・タイムゾーンを変更または確認できます。たとえば、Windows 7で次のタイムゾーン設定を選択できます。

(UTC+01:00)アムステルダム、ベルリン、ベルン、ローマ、ストックホルム、ウィーン

対応するTimeZoneKeyNameレジストリ・キーの値は、西ヨーロッパ標準時間です。

夏時間の自動調整の確認

グラフィカル・ユーザー・インタフェース(GUI)またはWindowsレジストリを通じて、夏時間の自動調整が有効かどうかを確認できます。

- GUIによる方法: コントロール パネルを使用して夏時間の自動調整が有効かどうかを確認するには:

1. Windowsの「スタート」ボタン、「コントロール パネル」の順にクリックします。

2. 「日付と時刻」をクリックします。

3. 「タイム ゾーンの変更」ボタンをクリックします。

4. 「自動的に夏時間の調整をする」というラベルの付いたチェック・ボックスが表示されます。このチェック・ボックスが選択されているかどうかを確認し、必要に応じて設定を変更します。

5. 「OK」をクリックします。「日付と時刻」ダイアログ・ボックスに戻ります。

- Windowsレジストリによる方法: Windowsレジストリ・エディタを実行して夏時間の自動調整が有効かどうかを確認できます。

注意:

Windowsレジストリを確認または編集する前にバックアップを取っておくことをお薦めします。間違った操作をすると、Windowsレジストリが損なわれる可能性があります。

Windowsレジストリから夏時間の自動調整を有効にするには:

1. Windowsの「スタート」ボタンをクリックします。

2. 「プログラムとファイルの検索」フィールドで、「regedit」と入力してから[Enter]キーを押して、レジストリ・エディタを開きます。

3. レジストリ・エディタで、DynamicDaylightTimeDisabledというキーを探し、その設定を調べます。
レジストリ設定が1の場合、動的な夏時間は無効になっています。

レジストリ設定が0の場合、動的な夏時間は有効になっています。

お好みで、Windowsコマンド・ウィンドウからWindowsレジストリにアクセスすることもできます。

次の例では、レジストリ設定は1です。この設定では、時計は夏時間に合わせて自動的に調整されません。

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation]
"DynamicDaylightTimeDisabled"=dword:00000001

DynamicDaylightTimeDisabledオプションを無効にした場合、JavaはGMT (グリニッチ標準時間)オフセットを返し、統一されたネーミング規則と互換性のあるタイムゾーンID(「Europe/Berlin」など)を返しません。たとえば、このオフセットは「Europe/Berlin」ではなくGMT+01として表されます。

コントロール・パネルでのデフォルト・タイムゾーンの設定

Windowsコントロール パネルを使用して、システムのデフォルト・タイムゾーンを変更または確認できます。

コントロール・パネルからシステムのデフォルト・タイムゾーンを設定するには:

1. Windowsの「スタート」ボタン、「コントロール パネル」の順にクリックします。

2. 「日付と時刻」をクリックします。

3. 「タイム ゾーンの変更」ボタンをクリックします。

4. 「タイム ゾーン」メニューから、希望するタイムゾーンを選択します。

5. 「OK」をクリックします。「日付と時刻」ダイアログ・ボックスに戻ります。

6. 「OK」をクリックして「日付と時刻」ダイアログ・ボックスを閉じます。

たとえば、Windows 7で次のタイムゾーンを選択できます。

(UTC)+1:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna.

対応するTimeZoneKeyNameレジストリ・キーの値は、西ヨーロッパ標準時間です。

-Duser.timezoneシステム・プロパティの確認

user.timezoneと呼ばれるJavaシステム・プロパティを使用することにより、コマンド行でデフォルト・タイムゾーンを明示的に設定できます。これにより、Windowsオペレーティング・システムでの設定が省略され、回避方法となることもあります。たとえば、システム上で実行されている1つのJavaプログラムでのみ夏時間(DST)を適用する場合にこの設定が役立ちます。

次の例では、Windowsの「コマンド プロンプト」ウィンドウからDefaultTimeTestZoneと呼ばれるJavaプログラムを実行することで、-Duser.timezoneシステム・プロパティを表示します。

c:\tztest> java -Duser.timezone=America/New_York DefaultTimeZone America/New_York

-Duser.timezoneを指定することでデフォルト・タイムゾーンを明示的に設定することがDefaultTimeTestZoneプログラムには有効だが、使用しているプログラムには有効でない場合、実行時に次のようなメソッド呼出しを使用してコードがデフォルトのJavaタイムゾーンを上書きするかどうかを確認するようにしてください。

TimeZone.setDefault(TimeZone zone)

Windows 7の特別なツール

Windows 7では、tzutil.exeと呼ばれるツールを使用できます。このツールを使用すると、手動でレジストリを確認することなく、現在のタイムゾーンIDの略称を要求できます。

tzutil.exeの実行例を次に示します。最初の行は、Windowsの「コマンド プロンプト」ウィンドウで入力するコマンドです。2番目の行はシステム・レスポンスです。

tzutil /g

W. Europe Standard Time

タイムゾーン・マッピングのJRE内部表現

Windowsでは、JREは<java-home>\lib\tzmappingsファイルを使用してWindowsタイムゾーンとJavaタイムゾーンのマッピングを表現します。このファイルの各行には4つのトークンがあります。最初のトークンは、TimeZoneKeyNameと呼ばれるWindowsタイムゾーンのレジストリ・キーです。「コントロール・パネルでの設定の確認」を参照してください。

2番目のトークンは、タイムゾーン・マップIDです。(これはWindows VistaおよびWindows 7では使用されません。)3番目のトークンはロケールです。4番目のトークンはJavaタイムゾーンIDを表しています。重要なトークンは、1番目、3番目(空の場合もある)、および4番目のトークンです。(注意: このファイルはパブリック・インタフェースではありません。)

Windowsのコントロール パネルで「(UTC+01:00)アムステルダム、ベルリン、ベルン、ローマ、ストックホルム、ウィーン」と呼ばれるタイムゾーンを選択した場合、tzmappingsファイルの関連する行は次のようになります。

W. Europe Standard Time:2,3::Europe/Berlin:

この例では、JREはデフォルトのタイムゾーン(4番目のトークン)を「Europe/Berlin」と認識します。

tzmappingsファイルに適切なマッピング・エントリが存在しない場合、MicrosoftがWindows更新で新しいタイムゾーンを導入したが、その新しいタイムゾーンがJREで使用できない可能性があります。この場合、JREのバグ・レポートを提出し、オラクルのJavaバグWebサイトからtzmappingsファイルの新しいエントリを要求できます。

tzedit.exeツールを実行すると、オペレーティング・システムとJREの間で同様の切断が発生する可能性があります。このツールはMicrosoftによってインターネットに投稿されており、ユーザーが新しいタイム・ゾーンを追加できます。JREで、このツールによってタイムゾーンがシステムに導入される可能性はありません。この場合も、解決方法はバグを提出して、新しいエントリがtzmappingsファイルに追加されるように要求することです。

第IV部 クライアントの問題のデバッグ

この部では、Javaクライアントの問題、トラブルシューティング方法およびクライアントの問題のデバッグ・ヒントについて説明します。内容は次のとおりです。

- クライアントの問題の概要
Javaクライアント・テクノロジの概要、Javaクライアントの問題およびトラブルシューティング・ヒントについて説明します。

- AWT
Java SE Abstract Window Toolkit (AWT)で発生する問題をデバッグするための、具体的な手順に関するガイダンスを提供します。

- Java 2D
Java 2D APIで発生するいくつかの一般的な問題のトラブルシューティングに関する指針を説明します。

- Swing
Java SE Swing APIで発生するいくつかの一般的な問題のトラブルシューティングに関する指針を説明します。

- 国際化
Java国際化で発生するいくつかの問題のトラブルシューティングに関する指針を説明します。

- Java Sound
Java Soundテクノロジに関する問題とその原因について説明し、回避方法を提示します。

- アプレットとJava Web Startアプリケーション
Javaアプリケーションとアプレットのデプロイに関する問題、トラブルシューティング・ヒントおよび解決策について説明します。

9 クライアントの問題の概要

この章では、様々なJava SE Desktopテクノロジ間の相互作用について説明します。この章ではさらに、問題のトラブルシューティングの開始場所となるテクノロジをユーザーが特定するための手助けをし、一般的なトラブルシューティング・ヒントも提供します。

この章の構成は、次のとおりです。

- Java SE Desktopテクノロジ

- 一般的な問題のトラブルシューティング手順

- 問題のタイプの特定

- 基本ツール

- Java Debug Wire Protocol

Java SE Desktopテクノロジ

Java SE Desktopは、リッチ・クライアント・アプリケーションおよびアプレットの作成に使用するいくつかのテクノロジで構成されています。

図9-1に示すように、デスクトップ・ツールおよびライブラリは、Javaアプリケーションとプラットフォーム上のコア・ツールおよびライブラリとの間のインタフェースを提供します。

図9-1 Java SE Desktopの概要

Java SEで使用可能なデスクトップ・テクノロジの詳細は、Java SE Desktopの概要ドキュメントを参照してください。

このトピックでは、次のJava SE Desktopテクノロジのトラブルシューティング手法について説明します。

- Abstract Window Toolkit (AWT)は、メニュー、ボタン、テキスト・フィールド、ダイアログ・ボックス、チェックボックスなどのグラフィカル・ユーザー・インタフェース(GUI)コンポーネントを構築し、それらのコンポーネントからのユーザー入力を処理するための一連のアプリケーション・プログラミング・インタフェース(API)を提供しています。また、AWTを使用すると、楕円や多角形などの簡単な図形をレンダリングしたり、アプリケーションで使用するインタフェースのレイアウトやフォントを開発者が制御したりすることもできます。さらに、ネイティブ・プラットフォームのクリップボードを介したカット・アンド・ペーストを可能にするデータ転送クラス(ドラッグ・アンド・ドロップを含む)も含まれています。
このAPIのクラスはソフトウェア・スタックの最下部(ベースとなるオペレーティング・システムやデスクトップ・システムにもっとも近い位置)にあります。

AWTは一連の重量コンポーネントも提供します。

純粋なAWTアプリケーションは通常、Swingに関係しません。AWTアプリケーションでカスタム・レンダリングを行う場合は、Java 2Dが使用されます。

- Java 2Dは、2次元の高度なグラフィックスとイメージングを処理する一連のクラスです。ライン・アート、テキスト、およびイメージが、1つの総合モデルの中に含まれています。このAPIは、イメージの合成とアルファ・チャンネル・イメージ、正確な色空間の定義と変換用のクラス・セット、および表示用イメージング演算子の豊富なセットを幅広くサポートします。java.awtおよびjava.awt.imageパッケージに追加した形で提供されます。
AWTと同様に、Java 2Dもソフトウェア・スタックの最下部(ベースとなるオペレーティング・システムやデスクトップ・システムにもっとも近い位置)にあります。

- Swingは、商用品質のデスクトップおよびインターネット/イントラネット・アプリケーションの開発を可能にする包括的な一連のGUIコンポーネントおよびサービスを提供します。
Swingは、他の多くのJava SE Desktopテクノロジ(AWT、Java2D、および国際化など)の上部に構築されています。ほとんどの場合、AWTのコンポーネントではなくSwingの上位レベル・コンポーネントの使用が推奨されます。ただし、AWTには、Swingでのプログラミング時に理解しておくことが重要なAPIが多数含まれています。

Swingは軽量ツールキットなので、ネイティブ・プラット・フォームとの対話処理はほとんどありません。SwingはレンダリングにJava 2Dを使用し、AWTがWindow、Frame、Dialogなどのトップレベル・コンポーネントの作成および操作機能を提供します。

- 国際化とは、簡単でコスト効率よく、とりわけソフトウェアの技術的な変更をせずに、ソフトウェアを様々な言語および地域に適合(ローカライズ)できるようにするためのソフトウェア設計プロセスのことです。ローカライズを行うには、翻訳されたテキスト、ロケール固有の動作を記述するデータ、フォント、入力方式など、ロケール固有のコンポーネントを追加するだけです。
Java SEでは、言語や文化に依存する機能を提供するクラスやパッケージに国際化サポートが完全に組み込まれています。

Java SEの国際化APIおよび機能の詳細は、国際化のドキュメントを参照してください。

- Java Soundは、拡張可能かつ柔軟なフレームワークにおける、オーディオの再生や取込み(録音)、ミキシング、MIDI(Musical Instrument Digital Interface)シーケンス、MIDI合成といったオーディオ操作の下位レベルのサポートを提供します。このAPIは、このプラットフォームでの高品質なオーディオ・ミキシングおよびMIDI合成を保証する効果的なサウンド・エンジンによってサポートされています。

- Java Plug-inは、一般的なWebブラウザをJavaプラットフォームに接続することで、それらのブラウザの機能を拡張します。この接続により、Webサイト上のJavaアプレットをデスクトップ上のWebブラウザ内で実行できます。

これらのテクノロジ間の関係の理解が深まるほど、問題が属する分野をすばやく特定できるようになります。

一般的な問題のトラブルシューティング手順

アプリケーションの問題をトラブルシューティングするための一般的な手順。

アプリケーションの実行中に問題が発生した場合は、次の手順に従って問題をトラブルシューティングします。

1. 症状の特定:

- 問題のタイプを特定します。

- 問題の分野を見つけます。

- 関連する構成情報を記録します。

2. 本質的でない問題の排除:

- 正しいパッチ、ドライバ、およびオペレーティング・システムがインストールされていることを確認します。

- 以前のリリースを試します(バックトレース)。

- テストを最小限にします。一度にテストする問題の数をできるだけ制限します。

- ハードウェアおよびソフトウェアの構成を最小化します。単一システムや複数システム上で問題を再現できるかを確認します。ブラウザのバージョンによって問題が変化するかを確認します。

- VMが複数インストールされているかどうかに問題が依存するかを確認します。

3. 原因の調査:

- その分野での典型的な原因をチェックします。

- フラグを使ってデフォルトを変更します。

- トレースを使用します。

- 例外的な場合には、システム・プロパティを使ってペイント・システムの動作を一時的に変更します。

4. 修正する:

- 可能性のある回避方法を見つけます。

- バグを提出します。
バグ・レポートの提出方法に関する指針や、レポートで収集する必要があるデータに関する提案については、「バグ・レポートの提出」を参照してください。

- 設定を修正します。

- アプリケーションを修正します。

問題のタイプの特定

発生ている問題を識別し、原因と解決策を見いだすための指針。

まず、少し時間をかけて、発生している問題を分類します。そうすれば、問題の特定分野の識別、原因の発見、および最終的な解決方法や回避方法の決定が容易になります。

次の各項では、よくあるタイプの問題について説明します。

- Javaクライアントのクラッシュ

- パフォーマンスの問題

- 動作の問題

これらの中には明確でないものもありますが、すべての可能性を検討することや問題でない要素を除外することは、常に役立ちます。

Javaクライアントのクラッシュ

Javaクライアントがクラッシュすると、致命的エラーの発生時に取得された情報と状態を含むエラー・ログが作成されます。

エラー・ログ・ファイルのデフォルト名はhs_err_pid.logです。ここで、pidはクラッシュしたプロセスのプロセス識別子(PID)です。このファイルは、スタンドアロンJavaアプリケーションの場合は現在のディレクトリに作成され、Javaアプレットの場合はブラウザ・バイナリ・ディレクトリまたはユーザー・クライアント・フォルダに作成されます。

致命的エラー・ログの詳細は、「致命的エラー・ログ」を参照してください。

ヘッダー・セクションの先頭付近の行には、エラーが発生したライブラリが示されます。次の例では、クラッシュがAWTライブラリに関係していたことがわかります。

...
# Java VM: Java HotSpot(TM) Client VM (1.6.0-beta2-b76 mixed mode, sharing)
# Problematic frame:
# C  [awt.dll+0x123456]
...

クラッシュがJava Native Interface (JNI)で発生した場合、その原因はおそらくデスクトップ・ライブラリです。ネイティブ・ライブラリでのクラッシュは通常、Java 2DまたはAWTでの問題を意味しますが、それは、Swingにはネイティブ・コードがあまり含まれていないからです。Swingに含まれる少量のネイティブ・コードはネイティブ・ルック・アンド・フィールに関するものなので、アプリケーションでネイティブ・ルック・アンド・フィールが使用されている場合はこの分野に関するクラッシュである可能性があります。

エラー・ログでは通常、クラッシュが発生した具体的なライブラリが示されるため、この情報から原因を推測できます。Java Development Kit (JDK)の一部でないライブラリでのクラッシュは通常、環境の問題(不正なビデオ・ドライバやデスクトップ・マネージャなど)を示しています。

パフォーマンスの問題

パフォーマンスの問題は、得られる情報が一般にあまり多くないため、診断が難しくなります。

まず、どのテクノロジで問題が発生しているかを判断する必要があります。たとえば、レンダリング・パフォーマンスの問題はおそらくJava 2Dの中にあり、応答性の問題はSwingに関連します。

パフォーマンス関連の問題は、次のカテゴリに分けられます。

- 起動
アプリケーションが起動してユーザーが使える状態になるまで、どれくらいの時間がかかりますか。

- フットプリント
アプリケーションのメモリー使用量はどれくらいですか。これは、Windowsのタスク・マネージャ、またはOracle SolarisおよびLinuxオペレーティング・システムのtopやprstatのようなツールで測定できます。

- 実行時
アプリケーションが実行するよう設計されているタスクを完了する速度はどのくらいですか。たとえば、アプリケーションで何かを計算する場合、その計算が終了するまでに要する時間はどのくらいですか。ゲームの場合、フレーム・レートが許容されていますか、またアニメーションはスムーズに動いていますか。

注意: これは、次のトピックの応答性と同じではありません。

- 応答性
アプリケーションがユーザーとの対話に応答する速度はどれくらいですか。ユーザーがメニューをクリックした場合、メニューが表示されるまでどれくらいの時間がかかりますか。実行時間の長いタスクに割り込むことはできますか。アプリケーションの再ペイント速度は、アプリケーションの動作が遅く見えない程度に高速ですか。

動作の問題

アプリケーションの様々な問題に対処するための指針。

クラッシュのほかに、動作に関係するさまざまな問題が発生する可能性があります。これらの問題のいくつかを次に示します。それらの問題の説明を通じて、トラブルシューティングを行うためのJava SE Desktopテクノロジについても説明していきます。

- ハングアップは、アプリケーションがユーザー入力への応答をやめたときに発生します。「プロセスのハングおよびループのトラブルシューティング」を参照してください。

- Javaコードの例外は、目に見える形でコンソールやアプリケーション・ログ・ファイルにスローされます。この出力を調べれば問題の分野がわかります。

- レンダリングおよび再ペイントの問題は、Java 2DまたはSwingに問題があるためです。たとえば、あるアプリケーションの外観が、そのアプリケーション上に別のアプリケーションをドラッグしたことによって発生した再ペイント後におかしくなることがあります。その他の例として、不適切なフォント、間違ったカラー、スクロール、あるアプリケーションのフレーム上に別のウィンドウをドラッグしたことによってアプリケーションのフレームが破損する問題、破損した領域の更新、などが挙げられます。
簡単なテスト方法を次に示します。問題が別のプラットフォーム上で再現可能な場合(たとえば、最初にWindows上で確認された問題がOracle SolarisやLinux上でも存在する場合)、それはSwingのPaintManagerの問題である可能性が非常に高くなります。

Java 2Dのレンダリング・パイプラインをいくつかのフラグで変更する方法については、「Java 2D」を参照してください。これは、問題がJava 2D、Swingのどちらに関係しているかを判断する際にも役立つ可能性があります。

マルチ画面に関する再ペイント問題(ある画面から別の画面にウィンドウを移動した際の再ペイントの問題や、デフォルト以外の画面デバイスとの対話処理に起因するその他の異常な動作など)は、Java 2Dに属します。

- デスクトップの対話処理に関連する問題は、AWTでの問題を示します。そのような問題のいくつかの例は、ウィンドウの移動、サイズ変更、最小化および最大化、フォーカスの処理、複数の画面の列挙、モーダリティの使用、通知領域(システム・トレイ)との対話処理、およびスプラッシュ画面の表示の際に発生します。

- ドラッグ・アンド・ドロップの問題はAWTに関連しています。

- 出力の問題は、使用しているAPIに応じて、Java 2DまたはAWTのどちらかに関連している可能性があります。

- AWTアプリケーションでのテキスト・レンダリングの問題は、おそらくフォント・プロパティまたは国際化の問題です。
ただし、アプリケーションでAWTのみが使用されている場合、テキスト・レンダリングの問題はJava 2Dに起因している可能性もあります。Oracle SolarisまたはLinuxの場合、テキスト・レンダリングはJava 2Dによって実行されます。

Swingのテキスト・レンダリングはJava 2Dによって実行されます。したがって、Swingを使用したアプリケーションでテキスト・レンダリングの問題(グリフの欠落、グリフの不正なレンダリング、行間や文字間の間隔の異常、フォントの低品質なレンダリングなど)が発生する場合、その問題はおそらくJava 2Dに属します。

- ペイントの問題は、Swingの問題である可能性がもっとも高くなります。

- フルスクリーンの問題は、Java 2D APIに関連しています。

- エンコーディングやロケールの問題(ロケール固有の文字が表示されないなど)は、国際化の問題を示します。

基本ツール

特定のタイプの問題のトラブルシューティングの助けになる可能性がある基本ツールのリスト。

この項では、特定タイプの問題のトラブルシューティングに役立つ可能性のある、いくつかのツールの一覧を示します。

- パフォーマンス: ベンチマーク、プロファイラ、DTrace、Javaプローブ。

- フットプリント: jmap、プロファイラ

- クラッシュ: ネイティブ・デバッガ

- ハング: JConsole、jstack、[Ctrl]+[Break]

- フォント・レンダリング: Font2DTest (JDKのdemo/jfc/Font2DTestに含まれている)

Java Debug Wire Protocol

Java Debug Wire Protocol(JDWP)は、アプリケーションやアプレットのデバッグに非常に役立ちます。

JDWPを使用してアプリケーションをデバッグするには、次の手順に従います。

1. コマンド行を開き、PATH環境変数をjdk/binに設定します。ここで、jdkはJDKのインストール・ディレクトリです。

2. 次のコマンドを使用して、デバッグするアプリケーション(この例ではTestという名前)を実行します。

- Windowsの場合:

java -Xdebug -Xrunjdwp:transport=dt_shmem,address=debug,server=y,suspend=y Test

- Oracle SolarisおよびLinuxオペレーティング・システムの場合:

java -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=y Test

Testクラスがデバッグ・モードで起動し、アドレスdebug (Windowsの場合)または8888 (Oracle SolarisおよびLinuxオペレーティング・システムの場合)でデバッガの接続を待機します。

3. コマンド行を新たに開き、次のコマンドを使用してjdbを実行し、実行中のデバッグ・サーバーに接続します。

- Windowsの場合:

jdb -attach 'debug'

- Oracle SolarisおよびLinuxオペレーティング・システムの場合:

jdb -attach 8888

jdbが初期化され、Testに接続されると、Javaレベルのデバッグを実行できます。

4. ブレークポイントを設定して、アプリケーションを実行します。たとえば、Testのmainメソッドの先頭にブレークポイントを設定するには、次のコマンドを実行します。

stop in Test.main run

jdbユーティリティがそのブレークポイントに達すると、アプリケーションが実行されている環境を調べて、予想どおりに機能しているかどうかを確認できます。

5. (オプション) Javaレベルのデバッグとともにネイティブ・レベルのデバッグも実行するには、JDWPで実行中のJavaプロセスにネイティブ・デバッガを使って接続します。

- Oracle Solarisではdbxユーティリティを、Linuxではgdbユーティリティをそれぞれ使用できます。

- Windowsでは、次のように、ネイティブ・レベルのデバッグにVisual Studioを使用できます。

a. Visual Studioを開きます。

b. 「デバッグ」メニューで、「プロセスにアタッチ」を選択します。JDWPで実行中のJavaプロセスを選択します。

c. 「プロジェクト」メニューで、「設定」を選択して、「デバッグ」タブを開きます。「カテゴリ」ドロップダウン・リストで、「追加のDLL」を選択し、デバッグするネイティブDLLを追加します(例: Test.dll)。

d. Test.dllのソース・ファイル(1つ以上)を開き、ブレークポイントを設定します。

e. jdbウィンドウでcontと入力します。プロセスがVisual Studioのブレークポイントに達します。

JDWPを使用してアプレットをデバッグするには、次の手順に従います。

1. Javaコントロール・パネルを起動し、「Java」タブを開いて、「表示」をクリックします。「Java Runtime Environment設定」ウィンドウで、必要なプラットフォームの「ランタイム・パラメータ」フィールドに次を指定します。

- Windowsの場合:

Djavaplugin.trace=true -Xdebug -Xrunjdwp:transport=dt_shmem,address=debug,server=y,suspend=y

- Oracle SolarisおよびLinuxオペレーティング・システムの場合:

Djavaplugin.trace=true -Xdebug -Xrunjdwp:transport=dt_shmem,address=8888,server=y,suspend=y

Webブラウザを起動してアプレットをロードすると、Java Plug-inがデバッグ・モードで起動し、アドレスdebug (Windowsの場合)または8888 (Oracle SolarisおよびLinuxオペレーティング・システムの場合)でデバッガの接続を待機します。

2. コマンド行を開き、次のコマンドを使用してjdbを実行し、実行中のデバッグ・サーバーに接続します。

- Windowsの場合:

jdb -attach 'debug'

- Oracle SolarisおよびLinuxオペレーティング・システムの場合:

jdb -attach 8888

jdbが初期化され、Testに接続されると、Javaレベルのデバッグを実行できます。

3. ブレークポイントを設定して、アプレットを実行します。たとえば、MyAppletのfunc1メソッドの先頭にブレークポイントを設定するには、次のコマンドを実行します。

stop in MyApplet.func1 run

jdbユーティリティがそのブレークポイントに達すると、アプリケーションが実行されている環境を調べて、予想どおりに機能しているかどうかを確認できます。

10 AWT

この章では、Java SE Abstract Window Toolkit (AWT)で発生する可能性のある一般的な問題をトラブルシューティングするための、具体的な手順および指針について説明します。

この章には、次のセクションがあります。

- AWTのデバッグに関するヒント

- レイアウト・マネージャの問題

- キー・イベント

- モーダリティの問題

- AWTのクラッシュ

- フォーカス・イベント

- データ転送

- その他の問題

- 重量または軽量コンポーネントの混在

AWTのデバッグに関するヒント

AWTに関する問題のデバッグに役立つヒント。

AWTコンポーネント階層をダンプするには、[Ctrl]+[Shift]+[F1]を押します。

アプリケーションがハングアップした場合、Windowsでは[Ctrl]+[Break]を押し(SIGBREAKシグナルが送信される)、Oracle SolarisおよびLinuxオペレーティング・システムでは[Ctrl]+[\]を押します(SIGQUITシグナルが送信される)。

Oracle SolarisおよびLinuxオペレーティング・システムでX11エラーをトレースするには、sun.awt.noisyerrorhandlerシステム・プロパティをtrueに設定します。Java SE 6以前のリリースでは、NOISY_AWT環境変数がこの目的で使用されていました。

Java SE 8より前は、AWTのイベント・ディスパッチ・スレッド(EDT)でスローされた例外をキャッチするには、システム・プロパティsun.awt.exception.handlerにpublic void handle(Throwable)メソッドを実装するクラスの名前を設定していました。Java SE 8ではこのメカニズムが更新され、標準のThread.UncaughtExceptionHandlerインタフェースが使用されるようになりました。

AWTの問題をデバッグする際には、ロガーによって生成される出力が役立ちます。詳細は、パッケージjava.util.loggingの説明を参照してください。

使用可能なロガーは次のとおりです。

- java.awt

- java.awt.focus

- java.awt.event

- java.awt.mixing

- sun.awt

- sun.awt.windows

- sun.awt.X11

レイアウト・マネージャの問題

レイアウト・マネージャに関連して発生する可能性がある問題、回避方法がある場合はその方法。

レイアウト・マネージャおよび回避策で次の問題が発生します。

1. invalidate()およびvalidate()を呼び出すとコンポーネントのサイズが増える

原因: GridBagLayoutレイアウト・マネージャのある特性のために、ipadxまたはipadyが設定された状態でinvalidate()とvalidate()が呼び出されると、コンポーネントのサイズがipadxまたはipadyの値まで増えます。その理由は、コンテナ内にコンポーネントを格納するために必要な容量を、GridBagLayoutレイアウト・マネージャが反復計算するからです。

回避方法: JDKでは、そのような場合にレイアウト・マネージャがコンポーネントを再配置する必要があるかどうかを検出する信頼できる簡単な方法がありませんが、簡単な回避方法が1つあります。次の例に示すように、現在必要なサイズを返すようにオーバーライドされるメソッドgetPreferredSize()を持つコンポーネントを使用します。

public Dimension getPreferredSize(){
return new Dimension(size+xpad*2+1, size+ypad*2+1);
}

2. Container.doLayout()メソッドからvalidate()を呼び出すと、無限再帰が発生する

原因: Container.doLayout()メソッドからvalidate()を呼び出すと、AWT自身がvalidate()からdoLayout()を呼び出すため、無限再帰が発生する場合があります。

キー・イベント

現在のリリースでは解決策がないキー・イベントの処理に関する問題。

現在解決されていないキーボードの問題は、次のとおりです。

- 一部の非英語キーボードでは特定のアクセント・キーがキーに刻印されているため、それらはプライマリ・レイヤーの文字になります。ところが、それらに対応するJavaキー・コードがないため、ニーモニックとして使用できません。

- 実行時にデフォルトのロケールを変更しても、メニューのアクセラレータ・キーとして表示されるテキストが変更されません。

- 標準109キー日本語キーボードでは、円記号キーとバックスラッシュ・キーのどちらの場合もバックスラッシュが生成されますが、これは両者のWM_CHARメッセージの文字コードが同じであるためです。AWTはそれらを区別すべきです。

Oracle Solaris 10およびLinux x86システムに関係するキーボードの問題を次に示します。

- これらのシステムでのキーボード入力は通常、X Window SystemのXキーボード拡張(XKB)に基づいています。ユーザーは、1つのキーボード・レイアウト(例: デンマーク語のdk)のみを構成することも、いくつかのレイアウト(例: usとdk)を構成して切り替えることもできます。

- sk、hu、czなどの一部のキーボード・レイアウトでは、数値入力キーパッドの小数点を押したときに、小数点の入力だけでなく1つ前の文字の削除も行われます。その原因はネイティブのバグにあります。回避方法は、usとskなど、2つのレイアウトを使用することです。この場合、どちらのレイアウトでも数値入力キーパッドは正しく動作します。

- キーボードの動的変更をサポートするUNIXシステム上で、実行中のJavaアプリケーションはそのような変更を認識しません。たとえば、キーボードをUSからドイツ語に変更しても、キーボード・マッピングは変更されません。Xサーバーはその変更を検出して対象クライアントにMappingNotifyイベントを送出しますが、AWTはキー・コード - キーシム・マッピングの情報をリフレッシュしません。

モーダリティの問題

モーダリティの使用に関連する問題についての情報

Java SE 6リリースでは、AWTモーダリティの領域で多くの問題が解決され、多くの改善が実装されました。Java SE 1.5以前のリリースでモーダリティの問題が発生している場合は、まず最新のJava SEリリースにアップグレードし、問題がすでに修正されているか確認してください。

Java SE 6で修正された問題のいくつかを次に示します。

- ブロックされたフレームの背後にモーダル・ダイアログ・ボックスが移動します。

- 同じウィンドウを親に持つ2つのモーダル・ダイアログ・ボックスが同時に開いていました。

この項では、次の問題について説明します。

- UNIXのウィンドウ・マネージャ:
一部のOracle SolarisまたはLinux環境(共通デスクトップ環境(CDE)のウィンドウ・マネージャを使用する場合など)では、モーダリティの改善点の多くは使用できません。Java SE 6以降のリリースで、あるモーダリティ・タイプやモーダル除外タイプが特定の構成でサポートされているかどうかを確認するには、次のメソッドを使用します。

- Toolkit.isModalityTypeSupported()

- Toolkit.isModalExclusionTypeSupported()

モーダル・ダイアログ・ボックスが画面に表示されると、ウィンドウ・マネージャは同じアプリケーション内の一部のJavaトップレベル・ウィンドウがタスクバーに表示されないようにすることがあります。これはエンド・ユーザーを困惑させることがありますが、非表示になっているウィンドウはすべてモーダル・ブロックされていて操作できないため、ユーザーの作業に大きな影響はありません。

- アプレット:
アプリケーションをブラウザ内のアプレットとして実行してモーダル・ダイアログ・ボックスを表示した場合、ブラウザのウィンドウがブロックされる可能性があります。このブロック処理の実装は、ブラウザやオペレーティング・システムごとに異なります。たとえば、WindowsではInternet ExplorerとMozilla Firefoxはどちらも正しく動作しますが、Oracle SolarisやLinuxオペレーティング・システムではMozilla Firefoxのウィンドウはブロックされません。

- モーダリティのその他の問題:
モーダリティ関連の機能とその使用方法の詳細は、AWTのモーダリティの仕様に関する項を参照してください。

その仕様の項の1つでは、モーダル・ダイアログ・ボックスに関係したりその影響を受けたりする可能性のあるAWT機能(常時最前面プロパティ、フォーカス処理、ウィンドウ状態など)をいくつか説明しています。そのような場合のアプリケーションの動作は通常、未定義となるかプラットフォームに依存するので、特定の動作をあてにしないでください。

AWTのクラッシュ

AWTに関連するクラッシュを識別してトラブルシューティングします。

- AWTクラッシュの判別:

クラッシュが発生すると、そのクラッシュの発生時に取得された情報や状態を含むエラー・ログが作成されます。「致命的エラー・ログ」を参照してください。

このファイルの先頭付近の行には、エラーが発生したライブラリが示されます。次の例は、クラッシュがAWTライブラリに関係していた場合のエラー・ログ・ファイルの一部を示しています。

...
# Java VM: Java HotSpot(TM) Client VM (1.6.0-beta2-b76 mixed mode, sharing)
# Problematic frame:
# C  [awt.dll+0x123456]
...

ただし、システム・ライブラリ内のどこか深い場所でクラッシュが発生しても、その原因がやはりAWTである場合もあります。このような場合、awt.dllが問題フレームとして指摘されず、次の例に示すように、ファイルのStack: Native frames: Java framesセクションをさらに調べる必要があります。

Stack: [0x0aeb0000,0x0aef0000),  sp=0x0aeefa44,  free space=254k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  0x00abc751
C  [USER32.dll+0x3a5f]
C  [USER32.dll+0x3b2e]
C  [USER32.dll+0x5874]
C  [USER32.dll+0x58a4]
C  [ntdll.dll+0x108f]
C  [USER32.dll+0x5e7e]
C  [awt.dll+0xec889]
C  [awt.dll+0xf877d]
j  sun.awt.windows.WToolkit.eventLoop()V+0
j  sun.awt.windows.WToolkit.run()V+69
j  java.lang.Thread.run()V+11
v  ~StubRoutines::call_stub
V  [jvm.dll+0x83c86]
V  [jvm.dll+0xd870f]
V  [jvm.dll+0x83b48]
V  [jvm.dll+0x838a5]
V  [jvm.dll+0x9ebc8]
V  [jvm.dll+0x108ba1]
V  [jvm.dll+0x108b6f]
C  [MSVCRT.dll+0x27fb8]
C  [kernel32.dll+0x202ed]

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  sun.awt.windows.WToolkit.eventLoop()V+0
j  sun.awt.windows.WToolkit.run()V+69
j  java.lang.Thread.run()V+11
v  ~StubRoutines::call_stub

ネイティブ・フレーム内のどこかにテキストawt.dllが現れた場合、そのクラッシュはAWTに関係している可能性があります。

- AWTクラッシュのトラブルシューティング:

AWTクラッシュの大部分はWindowsプラットフォーム上で発生しますが、それらの原因はスレッドのトレースです。これらの問題の多くがJava SE 6で修正されたので、以前のリリースでクラッシュが発生した場合はまず、その問題がすでに最新リリースで修正されているか確認してみてください。

考えられるクラッシュの原因の1つは、AWTの多くの操作が非同期であることです。たとえば、frame.setVisible(true)を呼び出してフレームを表示させる場合、この呼出しから戻った後でそのフレームがアクティブ・ウィンドウになっている保証はありません。

もう1つの例は、ネイティブのファイル・ダイアログに関するものです。オペレーティング・システムがこれらのダイアログを初期化して表示するまで多少の時間がかかるため、setVisible(true)を呼び出してすぐにそれらを破棄してしまうと、クラッシュが発生する可能性があります。したがって、アプリケーション内にいくつかのAWT呼出しがあり、それらが同時に実行されたり短時間に連続して実行されたりする場合には、呼出しの間にある程度の遅延を挿入したり、何らかの同期を追加したりすることをお薦めします。

フォーカス・イベント

フォーカス・イベントに関連するトラブルシューティングの問題。

次の項では、フォーカス・イベントに関連する問題のトラブルシューティングについて説明します。

- フォーカス・イベントをトレースする方法

- ネイティブ・フォーカス・システム

- Java Plug-inでのフォーカス・システム

- Xウィンドウ・マネージャでサポートされているフォーカス・モデル

- フォーカスのその他の問題

フォーカス・イベントをトレースする方法

フォーカスの問題のトラブルシューティング。

次の例に示すように、フォーカス・リスナーをツールキットに追加して、フォーカス・イベントをトレースできます。

Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener(
public void eventDispatched(AWTEvent e) {
System.err.println(e);
}
), FocusEvent.FOCUS_EVENT_MASK | WindowEvent.WINDOW_FOCUS_EVENT_MASK |
WindowEvent.WINDOW_EVENT_MASK);

ここでSystem.errストリームが使用されているのは、出力のバッファリングが行われないからです。

注意:フォーカス・イベントの正しい順序は次のとおりです。

- フォーカスを失うコンポーネントのFOCUS_LOST

- フォーカスを失うトップ・レベルのWINDOW_LOST_FOCUS

- アクティブ状態を失うトップ・レベルのWINDOW_DEACTIVATED

- アクティブ・ウィンドウになるトップ・レベルのWINDOW_ACTIVATED

- フォーカスされたウィンドウになるトップ・レベルのWINDOW_GAINED_FOCUS

- フォーカスを得るコンポーネントのFOCUS_GAINED

フォーカスされたウィンドウ内のコンポーネント間でフォーカスが移動する場合は、FOCUS_LOSTイベントとFOCUS_GAINEDイベントのみが生成されるはずです。同じ所有者が所有するウィンドウ間や、所有されるウィンドウとその所有者の間でフォーカスが移動する場合は、次のイベントが生成されるはずです。

- FOCUS_LOST

- WINDOW_LOST_FOCUS

- WINDOW_GAINED_FOCUS

- FOCUS_GAINED

注意:
フォーカスまたはアクティベーションを失うイベントが最初に来る必要があります。

ネイティブ・フォーカス・システム

時折、ネイティブ・プラット・フォームが原因で問題が発生することがあります。これをチェックするには、フォーカスに関係するネイティブ・イベントを調査します。

フォーカス対象のウィンドウがアクティブ化され、フォーカス対象のコンポーネントがネイティブ・フォーカス・イベントを受け取ることを確認してください。

Windowsプラットフォームのネイティブ・フォーカス・イベントは、次のとおりです。

- WM_ACTIVATE (トップ・レベル用)。WPARAMは、アクティブ化の際はWA_ACTIVE、非アクティブ化の際はWA_INACTIVEになります。

- WM_SETFOCUSおよびWM_KILLFOCUS (コンポーネント用)。

Windowsプラットフォームでは、合成フォーカスという概念が実装されています。つまり、フォーカス所有者コンポーネントはそのフォーカス可能状態をエミュレートするだけですが、実際のネイティブ・フォーカスはフォーカス・プロキシ・コンポーネントに設定されます。このコンポーネントは、キーとインプット・メソッドのネイティブ・メッセージを受け取り、それらをフォーカス所有者にディスパッチします。JDK7より前は、フォーカス・プロキシ・コンポーネントはフレームまたはダイアログ・ボックス内の専用の非表示の子コンポーネントでした。最新のJDKリリースでは、フレームまたはダイアログ・ボックスがフォーカス・プロキシとして機能します。現在は、所有されるウィンドウ内のコンポーネントだけでなく、すべての子コンポーネントのフォーカス・プロキシとしても機能します。単純なウィンドウは、ネイティブ・フォーカスを受け取ることは決してなく、その所有者のフォーカス・プロキシに依存します。ユーザーがこのメカニズムを意識することはありませんが、デバッグ時には考慮に入れるべきです。

Oracle SolarisおよびLinuxオペレーティング・システム上のXToolkitでは、AWT自体がフォーカスを管理できるフォーカス・モデルを使用しています。このモデルでは、ウィンドウ・マネージャが直接トップレベル・ウィンドウに入力フォーカスを設定することはなく、代わりにWM_TAKE_FOCUSクライアント・メッセージのみを送信することで、フォーカスを設定すべきであることを示します。その後、トップレベル・ウィンドウへのフォーカス設定が可能な場合は、その設定がAWTによって明示的に行われます。

注意:
Xサーバーおよび一部のウィンドウ・マネージャは、ウィンドウにフォーカス・イベントを送信することがあります。ただし、これらのイベントはAWTによって破棄されます。

トップ・レベル内のコンポーネントがフォーカスを得ても、AWTはフォーカス・イベントの階層チェーンを生成しません。さらに、コンポーネントにマップされたネイティブ・ウィンドウはネイティブ・フォーカス・イベントを取得しません。Oracle SolarisおよびLinuxプラットフォームではWindowsプラットフォームと同じく、AWTはフォーカス・プロキシ・メカニズムを使用します。したがって、コンポーネントのフォーカスはフォーカス・イベントの合成によって設定され、不可視のフォーカス・プロキシがネイティブ・フォーカスを保持します。

Windowオブジェクト(FrameオブジェクトでもDialogオブジェクトでもない)にマップされたネイティブ・ウィンドウには、override-redirectフラグが設定されます。したがって、ウィンドウ・マネージャはそのウィンドウに対し、フォーカス移動に関して通知しません。このウィンドウでフォーカスが要求されるのは、マウス・クリックへの応答としてだけです。このウィンドウはネイティブ・フォーカス・イベントを一切受け取りません。したがって、トレース可能なイベントは、フレームまたはダイアログ・ボックス上のFocusInまたはFocusOutイベントだけです。XToolkitでは、フォーカスの主な処理がJavaレベルで発生するため、フォーカスのデバッグがWToolkitの場合よりも単純になります。

Java Plug-inでのフォーカス・システム

アプレットは、EmbeddedFrameの子(ただし直接の子ではない)としてブラウザ内に埋め込まれます。

これは、プラグインとの通信機能を備えた特殊なFrameです。アプレットからは、EmbeddedFrameはトップ・レベルの完全なFrameに見えます。

EmbeddedFrameのフォーカスを管理するには、特殊なアクションが必要になります。アプレットが最初に起動する際に、EmbeddedFrameはネイティブ・システムによってデフォルトでアクティブ化されません。アクティブ化は、EmbeddedFrameに用意された特殊なAPIをプラグインがトリガーすることによって実行されます。フォーカスがアプレットを離れる際のEmbeddedFrameの非アクティブ化も、合成された方法で行われます。

Xウィンドウ・マネージャでサポートされているフォーカス・モデル

Xウィンドウ・マネージャでサポートされているフォーカス・モデルのリスト。

Xウィンドウ・マネージャでサポートされているフォーカス・モデルは、次のとおりです。

- Click-to-focusは一般的に使用されているフォーカス・モデルです。(たとえば、Microsoft Windowsではこのモデルが使用されています。)

- Focus-follows-mouseは、マウスの下にあるウィンドウにフォーカスが移動するフォーカス・モデルです。

Java SE 7のXAWTではfocus-follows-mouseモデルは検出されませんが、これにより、単純なウィンドウ(java.awt.Windowクラスのオブジェクト)で問題が発生します。そのようなウィンドウにはoverride-redirectプロパティが設定されているため、ウィンドウにフォーカスを移動できるのはマウス・ボタンを押した場合のみであり、ウィンドウの上にマウスを移動してもフォーカスは移動しません。回避方法としては、ウィンドウにMouseListenerを設定し、マウスがウィンドウのボーダーを横切ったときにウィンドウへのフォーカスを要求します。

フォーカスのその他の問題

AWTのフォーカスに関連して発生する可能性のある問題および解決方法の提案。

1. KDEを実行するLinux上のXToolkitでは、フレームのタイトルをクリックしても、2つのフレーム間で切り替えることができない。

フレームの内側にあるコンポーネントをクリックすると、フォーカスが変更されます。

解決方法: ウィンドウ・マネージャのバージョンをチェックし、3.0 以上にアップグレードします。

2. [Tab]/[Shift]+[Tab]でフォーカスを移動するためにKeyListenerを使用してフォーカスを管理しても、キー・イベントが現れない。

解決方法: トラバーサル・キー・イベントをキャッチするには、Component.setFocusTraversalKeysEnabled(true)を呼び出してそれらのイベントを有効にする必要があります。

3. Window.setModalExclusionType(ModalExclusionType)でウィンドウがモーダル除外に設定される。

その所有者であるフレームはモーダル・ブロックされています。この場合、ウィンドウもモーダル・ブロックされたままになります。

解決方法: ウィンドウは、その所有者がフォーカスの取得を許可されていない場合はフォーカスされたウィンドウになれません。解決方法は、所有者をモーダリティから除外することです。

4. Windowsで、コンポーネントがフォーカスを要求し、同時にそのコンテナから削除される。

java.lang.NullPointerException: null pDataがスローされる場合があります。

解決方法: 例外のスローを避けるもっとも簡単な方法は、削除とフォーカス要求をEDT上で行うことです。もう1つのより複雑なアプローチは、フォーカス要求と削除を同期することです(これらのアクションをそれぞれ異なるスレッド上で実行する必要がある場合)。

5. あるコンポーネントがフォーカスを要求してすぐにフォーカス所有者が削除された場合、その削除されたコンポーネントの後のコンポーネントにフォーカスが移動する。

たとえば、コンポーネントAがフォーカス所有者とします。コンポーネントBがフォーカスを要求し、その直後にコンポーネントAがそのコンテナから削除されます。フォーカスは最終的に、コンテナ内でコンポーネントAの後に配置されたコンポーネントCに移動し、コンポーネントBには移動しません。

解決方法: この場合、フォーカス要求がコンポーネントAの削除前ではなく削除後に実行されるようにしてください。

6. Windowsで、非アクティブなフレーム内のウィンドウをalwaysOnTopに設定すると、そのウィンドウはキー・イベントを受け取れない。

たとえば、フレームが1つと、そのフレームが所有するウィンドウが1つ表示されているとします。フレームがアクティブでないため、ウィンドウはフォーカスされません。その後、ウィンドウをalwaysOnTopに設定します。ウィンドウはフォーカスを取得しますが、その所有者は非アクティブなままです。したがって、ウィンドウはキー・イベントを受け取れません。

解決方法: ウィンドウをalwaysOnTopに設定する前に、フレームを前面に移動します(Frame.toFront()メソッド)。

7. スプラッシュ画面が表示され、そのスプラッシュ画面のウィンドウが閉じた後でフレームが表示されると、そのフレームはアクティブ化されない。

解決方法: フレームを表示した後で(Frame.setVisible(true)メソッド)、フレームを前面に移動します(Frame.toFront()メソッド)。

8. WindowFocusListener.windowGainedFocus(WindowEvent)メソッドがフレームの直近のフォーカス所有者を返さない。

たとえば、あるフレームがフォーカスされたウィンドウであり、そのコンポーネントの1つがフォーカス所有者であるとします。別のウィンドウがクリックされたあと、このフレームが再度クリックされます。WINDOW_GAINED_FOCUSがフレームに送信され、WindowFocusListener.windowGainedFocus(WindowEvent)メソッドが呼び出されます。ただし、このコールバックの内部では、Frame.getMostRecentFocusOwner()からnullが返されるため、フレームの直近のフォーカス所有者を確認できません。

解決方法: WindowListener.windowActivated(WindowEvent)コールバックの内部では、フレームの直近のフォーカス所有者を取得できます。ただし、この時点までにフレームがフォーカスされたウィンドウになっているのは、フレームが所有するウィンドウが1つも存在しない場合だけです。

注意:

このアプローチは、ウィンドウでは機能せず、フレームまたはダイアログ・ボックスでのみ機能します。

9. アプレットが起動時にフォーカスを横取りする。

解決方法: この動作は、JDKのデフォルト動作です。ただし、アプレットが起動時にフォーカスを取得しないようにする必要がある場合もあります(アプレットが不可視であるためにフォーカスが不要な場合など)。この場合は、次の例に示すように、HTMLタグ内で特殊パラメータinitial_focusをfalseに設定します。

<applet code="MyApplet" width=50 height=50>
<param name=initial_focus value="false">
</applet>

10. Component.setEnabled(false)でウィンドウを無効にしても、完全にフォーカス不可能な状態にならない。

解決方法: Component.setEnabled(false)またはComponent.setFocusable(false)を呼び出すことで設定された状態が、そのすべてのコンテンツでもフォーカス不可能な状態として維持されるとは仮定しないでください。代わりに、Window.setFocusableWindowState(boolean)メソッドを使用します。

データ転送

ドラッグ・アンド・ドロップ(DnD)およびカット・アンド・ペースト/コピー・アンド・ペースト(CCP)操作をアプリケーションに追加できるようにする、データ転送機能に関連して発生する可能性のある問題。

次の項では、データ転送機能で発生する可能性がある問題について説明します。

- ドラッグ・アンド・ドロップ・アプリケーションのデバッグ

- データ転送に関するよくある問題

ドラッグ・アンド・ドロップ・アプリケーションのデバッグ

ドラッグ・アンド・ドロップ(DnD)アプリケーションの問題のトラブルシューティングに使用できる方法。

デバッガを使用してDnD機能をトラブルシューティングするのは困難です。ドラッグ・アンド・ドロップ操作中はすべての入力がグラブされるからです。そのため、DnDの操作中にブレークポイントを入れた場合は、Xサーバーの再起動が必要になることがあります。かわりに、リモート・デバッグを使用してみてください。

DnDに関するほとんどの問題のトラブルシューティングには、2つの単純な方法を使用できます。

- すべてのDataFlavorインスタンスの出力

- 受信されたデータの出力
リモート・デバッグに代わる方法として、遅延なしで出力するSystem.err.println()関数があります。

データ転送に関するよくある問題

AWTのデータ転送操作で頻繁に発生する問題およびトラブルシューティング方法の提案。

1. 大量のデータをクリップボードからペーストするのに時間がかかりすぎる。

Clipboard.getContents()関数を使ってペースト操作を行うと、アプリケーションが少しの間、ハング・アップすることがあります(特にペースト対象データの提供元がリッチ・アプリケーションである場合)。

Clipboard.getContents()関数は、使用可能なすべてのタイプ(いくつかのテキスト・タイプやイメージ・タイプなど)に含まれるクリップボード・データを取得しますが、これはコストが高く、不要である可能性があります。

解決方法: Clipboard.getData()メソッドを使って特定のデータのみをクリップボードから取得します。1つのタイプまたは少数のタイプのみのデータが必要な場合は、getContents()のかわりに次のいずれかのClipboardメソッドを使用してください。

- DataFlavor[] getAvailableDataFlavors()

- boolean isDataFlavorAvailable(DataFlavor flavor)

- Object getData(DataFlavor flavor)

2. JavaアプリケーションでDnD操作にTransferable.getTransferData()を使用すると、ドラッグに長い時間がかかるように思われる。

転送データの初期化を必要な場合にのみ行えるよう、Transferable.getTransferData()内に初期化コードが配置されました。

Transferableデータの生成コストは高く、DnD操作中にTransferable.getTransferData()が複数回呼び出されるため、速度が低下します。

解決方法: Transferableデータをキャッシュに入れて、一度しか生成されないようにします。

3. JavaアプリケーションとGNOME/KDEデスクトップやファイル・ブラウザとの間でファイルを転送できない。

Windowsや一部のウィンドウ・マネージャでは、転送ファイルのリストはDataFlavor.javaFileListFlavorデータ・タイプとして表現できます。しかし、すべてのウィンドウ・マネージャがファイルのリストをこの形式で表現するわけではありません。たとえば、GNOMEウィンドウ・マネージャは、ファイル・リストをURIのリストとして表現します。

回避方法: ファイルを取得するには、String型のデータを要求した後、RFC 2483で説明されているtext/uri-list形式に従って文字列をファイルのリストに変換します。JavaアプリケーションからGNOME/KDEデスクトップやファイル・ブラウザにファイルをドロップできるようにするには、text/uri-list形式でデータをエクスポートします。例については、RFEの回避方法に関する項を参照してください。

解決方法: DnD操作中のマウス・カーソルの動きに合わせて、表面にイメージがレンダリングされたウィンドウを移動します。RFEの回避方法のセクションに記載されたコード例を参照してください。

4. DragGestureEventまたはDragSourceのstartDrag()メソッドの1つに渡したイメージが、後続のDnD操作中に表示されない。

5. DnDを使用して配列を移動する方法がない。

DataFlavorクラスには、配列を処理するコンストラクタはありません。配列のMIMEタイプには、エスケープする文字が含まれます。次の例のコードでは、IllegalArgumentExceptionがスローされます。

new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
"; class=" +
(new String[0]).getClass().getName())

解決方法: 次の例に示すように、表現クラスのパラメータの値を引用符で囲みます(引用符はエスケープします)。

new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
"; class=" +
"\"" +
(new String[0]).getClass().getName() +
"\"")

バグ・レポートを参照してください。

6. SwingコンポーネントでAWTのDnDサポートを使用すると、問題が発生する。

DnD操作時に奇妙なイベントが発生する、複数の項目をドラッグ・アンド・ドロップできない、InvalidDnDOperationExceptionがスローされるなど、様々な問題が発生する可能性があります。

解決方法: SwingコンポーネントではSwingのDnDサポートを使用します。SwingのDnD実装はAWTのDnD実装に基づいていますが、SwingとAWTのDnDを混在させることはできません。SwingのチュートリアルのDnDに関する項を参照してください。

7. ターゲットに依存するようにソースの状態を変更する方法がない。

ターゲットに依存するようにソースの状態を変更するには、ソース・コンポーネントとターゲット・コンポーネントへの参照が同じコード領域内で必要になりますが、これは現時点ではDnD APIに実装されていません。

回避方法: 回避方法の1つは、イベントのコンテキストを判定できるようにするためのフラグを、転送可能オブジェクトに追加することです。

1つのJava VM内でのデータ転送では、次の回避方法を提案します。

- ターゲット・コンポーネントをDragSourceListenerとして実装します。

- 次の例に示すように、DragGestureRecognizer.dragGestureRecognized()でターゲットをドラッグ・ソース・リスナーとして追加します。

public void dragGestureRecognized(DragGestureEvent dge) {
dge.startDrag(null, new StringSelection("SomeTransferedText"));
dge.getDragSource().addDragSourceListener(target);
}

- これで、DragSourceListener()のdragEnter()、dragOver()、dropActionChanged()、dragDropEnd()の各メソッド内で、ターゲットとソースを取得できます。

8. アプリケーション内でのオブジェクト転送に長い時間がかかる。

大量データの転送や転送オブジェクトの作成にかかる時間が長すぎます。ユーザーは、データ転送が完了するまで長時間待つ必要があります。

この高コスト処理で転送に時間がかかりすぎるのは、Transferable.getTransferData()が終了するまで待つ必要があるからです。

解決方法: この解決方法が有効なのは、1つのJava VM内でデータを転送する場合だけです。ドラッグ操作の前に、高コストのリソースを作成または取得しておきます。たとえば、Transferable.getTransferData()の処理時間が長くなりすぎないよう、転送可能なデータの作成時にファイルのコンテンツを取得します。

その他の問題

スプラッシュ画面の問題、ポップアップ・メニューの問題、背景色の継承など、AWTのその他の問題のトラブルシューティング。

次の各項では、他の問題をトラブルシューティングするためのヒントについて説明します。

- スプラッシュ画面の問題

- トレイ・アイコンの問題

- ポップアップ・メニューの問題

- 背景色または前景色の継承

- AWTパネルのサイズ制限

- X11上でポップアップ・メニューや類似コンポーネントのデバッグ中にハングアップする

- X11でのWindow.toFront()/toBack()の動作

スプラッシュ画面の問題

AWTのスプラッシュ画面で発生する可能性がある問題と解決策。

この項では、AWTのスプラッシュ画面で発生する可能性のあるいくつかの問題を説明します。

1. 適切なMANIFEST.MFを含むJARファイルを-classpathに指定しても、スプラッシュ画面が機能しない。
解決方法: 次の問題の解決方法を参照してください。

2. アプリケーションのいくつかのJARファイルのどれにスプラッシュ画面のイメージを含める必要があるかが明らかでない。
解決方法: スプラッシュ画面のイメージがJARファイルから選択されるのは、-jarコマンド行オプションでJARファイルが使用された場合のみです。このJARファイルには、「SplashScreen-Image」マニフェスト・オプションとイメージ・ファイルの両方を含めるようにしてください。-classpathのJARファイルのMANIFEST.MF内でスプラッシュ画面がチェックされることは決してありません。-jarを使用しない場合でも、コマンド行で-splashを使ってスプラッシュ画面のイメージを指定できます。

3. 透明度PNGを使用したスプラッシュ画面がOracle SolarisやLinuxオペレーティング・システムで機能しない。
解決方法: これは、X11のネイティブの制限です。Oracle SolarisおよびLinuxオペレーティング・システムでは、半透明イメージのアルファ・チャネルが50%のしきい値と比較されます。アルファ値が0.5より高いピクセルは不透明に、アルファ値が0.5より低いピクセルは完全に透明になります。

トレイ・アイコンの問題

トレイ・アイコンで発生する可能性がある問題。

Windows 98のJava SE 6リリースではTrayIcon.displayMessage()メソッドがサポートされていませんが、これは、バルーン表示用のネイティブ・サービスがWindows 98でサポートされていないからです。

SecurityManagerがインストールされている場合、TrayIconオブジェクトを作成するには、AWTPermissionの値がaccessSystemTrayに設定される必要があります。

ポップアップ・メニューの問題

ポップアップ・メニューで発生する可能性がある問題。

JPopupMenu.setInvoker()メソッドで、呼出し元(invoker)とは、ポップアップ・メニューの表示先となるコンポーネントのことです。このプロパティがnullに設定されていると、ポップアップ・メニューが正しく機能しません。

解決方法は、ポップアップの呼出し元を自分自身に設定することです。

背景色または前景色の継承

アプリケーションの一貫性をすべてのプラットフォームで確保するには、すべてのコンポーネントやコンテナで、明示的なカラー割り当て(背景と前景の両方)を使用してください。

多くのAWTコンポーネントでは、背景色や前景色として、親の色ではなく独自のデフォルトが使用されます。

この動作はプラットフォームに依存しています。つまり、プラットフォームが異なれば、同じコンポーネントでも違った動作になることがあります。また、コンポーネントの中には背景色または前景色のどちらか一方にはデフォルト値を使用するが、もう一方の色については親の値を取るものもあります。

AWTパネルのサイズ制限

AWTのコンテナにはサイズの制限があります。ほとんどのプラットフォームでは、この制限は32,767ピクセルです。

したがって、たとえばキャンバス・オブジェクトの高さが25ピクセルであれば、Java AWTパネル上に1310個を超える数のオブジェクトを表示することはできません。

残念ながら、この制限を変更する方法はなく、Javaコードやネイティブ・コードを使用しても変更できません。この制限は、オペレーティング・システムでウィジェット・サイズの格納に使用されるデータの型に依存します。たとえば、Windows 2000/XPオペレーティング・システムやLinuxオペレーティング・システムではinteger型が使用されるため、整数の最大サイズが制限値となります。他のオペレーティング・システムでは、longなど別の型が使用されている可能性があり、この場合であれば制限は高くなります。

ご使用のプラットフォームのドキュメントを参照してください。

役立つ可能性のあるこの制限の回避方法の例を次に示します。

- コンポーネントをページ単位で表示します。

- タブを使って一度に表示されるコンポーネントの数を減らします。

X11上でポップアップ・メニューや類似コンポーネントのデバッグ中にハングアップする

特定のグラフィカル・ユーザー・インターフェイス(GUI)コンポーネントのデバッグ時に-Dsun.awt.disablegrab=trueシステム・プロパティを設定します。

特定のグラフィカル・ユーザー・インタフェース(GUI)のアクションでは、そのアクションを終了させるタイミングを決めるために、すべての入力イベントをグラブする必要があります(ポップアップ・メニューの移動など)。占有が続いている間は、ほかのアプリケーションは入力イベントを一切受け取りません。Javaアプリケーションのデバッグ時に、グラブがアクティブの間にブレークポイントに達すると、オペレーティング・システムがハングアップしたようになります。これは、グラブを保持しているJavaアプリケーションがデバッガによって停止され、入力イベントを一切処理できなくなったために起こります。他のアプリケーションは、このインストールされたグラブが原因でイベントを受け取りません。そのようなアプリケーションのデバッグを可能にするには、デバッガからアプリケーションを実行する際に次のシステム・プロパティを設定するようにしてください。

-Dsun.awt.disablegrab=true

このプロパティにより事実上、グラブの設定がオフになるので、システムがハングアップしなくなります。ただし、このオプションが設定されていると、場合によっては、通常であれば終了するはずのGUIアクションが終了できなくなります。たとえば、ウィンドウのタイトル・バーをクリックしてもポップアップ・メニューが終了しない可能性があります。

X11でのWindow.toFront()/toBack()の動作

サードパーティ・ソフトウェア(特にMetacityなどのウィンドウ・マネージャ)から課される制限のため、toFront()/toBack()メソッドが予想どおりに機能せず、他のトップレベル・ウィンドウとの関係におけるウィンドウのスタック順序が変更されない可能性があります。

詳細については、CR 6472274を参照してください。

アプリケーションでウィンドウが手前に表示されるようにする場合は、まずWindow.setAlwaysOnTop(true)を呼び出して一時的にウィンドウが常に手前に表示されるようにしてから、setAlwaysOnTop(false)を呼び出して「常に手前に表示」の状態をリセットすることで、この問題の回避を試みることができます。

注意:

ウィンドウ・マネージャから課される制約が強化される可能性があるため、この回避方法の動作は保証されません。また、ウィンドウを「常に手前に表示」に設定できるのは信頼できるアプリケーションのみです。サンドボックスで実行されている署名なしのアプレットまたは署名なしのWeb StartアプリケーションはこのAPIを使用できないため、この問題を回避できません。

ただし、ネイティブ・アプリケーションも似た問題に悩まされているので、この特殊性により、Javaアプリケーションの動作はネイティブ・アプリケーションと似たものになっています。

重量または軽量コンポーネントの混在

重量または軽量(HW/LW)コンポーネントの混在機能で発生する問題。

重量または軽量(HW/LW)コンポーネントの混在機能では、次の問題を扱います。

- コンポーネント階層の有効化:

コンポーネントのレイアウト関係のプロパティ(サイズ、位置、フォントなど)を1つでも変更すると、コンポーネントとその祖先が無効にされます。HW/LW混在機能が正しく機能するには、そのような変更を行なったあとでコンポーネント階層を有効にする必要があります。無効化はデフォルトで、階層内の最上位コンテナ(Frameオブジェクトなど)で停止します。したがって、階層の有効性を復元するには、アプリケーションからFrame.validate()メソッドを呼び出すようにしてください。次に例を示します。

component.setFont(myFont);
frame.validate();

frameはcomponentを含んでいるフレームのことです。

注意:
SwingアプリケーションやSwingライブラリでは、多くの場合、次のパターンが使用されます。

component.setFont(myFont);
component.revalidate();

このrevalidate()呼出しは十分ではありません。この呼出しではコンポーネントのもっとも近い有効なルートから始まる階層のみを有効化するため、それより上位のコンテナが無効のままになっているからです。その場合、HW/LW機能はHWコンポーネントの正しい形状を計算できず、視覚的なアーティファクトが画面に表示される可能性があります。

コンポーネント階層全体の有効性を検証するには、「AWTのデバッグに関するヒント」で説明した[Ctrl]+[Shift]+[F1]というキーの組合せを使用できます。コンポーネントに「invalid」というマークが付いている場合、validate()呼出しがどこかで欠落していることを示している可能性があります。

- 有効なルート:

コンポーネント階層の有効化のプロセスにかなりの時間を要することがあるため、プロセスの処理速度を上げるために、「コンポーネント階層の有効化」で説明した有効なルートという概念がSwingに取り入れられました。そのような最適化では階層の上位部分が無効のままになりますが、有効なルートの内側にあるコンポーネントのレイアウトはコンポーネント階層の外側(つまり、有効なルートの兄弟)のレイアウトに影響を与えないため、問題は起きませんでした。ただし、1つの階層内にHWコンポーネントとLWコンポーネントが混在している場合、この記述は当てはまらなくなります。そのため、この機能ではコンポーネント階層全体が有効になる必要があります。

frame.validate()の呼び出しは効率的ではない可能性があるため、AWTでは、コンポーネント階層の無効化/有効化を処理するための最適化された代替手段がサポートされています。この機能を有効にするには、システム・プロパティを使用します。

-Djava.awt.smartInvalidate=true

このプロパティが指定されると、invalidate()メソッドは、invalidate()メソッドが呼び出されたコンポーネントのもっとも近い有効なルートに達した時点で、階層の無効化を停止するようになります。その後、コンポーネント階層の有効性を復元するには、アプリケーションから次を呼び出すだけです。

component.revalidate();

注意:
この場合は、frameが引き続き有効であるため、frame.validate()の呼出しは事実上操作なし(何も行わない文)になります。一部のアプリケーションは、階層の有効なルート(フレームなど)より上位にあるコンポーネントで直接validate()を呼び出すことに依存しているため、この新しい最適化された動作によって非互換性の問題が発生する可能性があり、したがってこの動作を使用できるのはシステム・プロパティを指定した場合のみです。

この最適化された新しいモードでアプリケーションを実行する際になんらかの問題が発生する場合は、「AWTのデバッグに関するヒント」で説明した[Ctrl]+[Shift]+[F1]というキーの組合せを使用することで、コンポーネント階層のどの部分が無効なままになっており、したがって問題の原因となっている可能性があるのかを調査できます。

- Swingのペイントの最適化:

デフォルトでは、Swingライブラリはコンポーネント階層内にHWコンポーネントが1つも存在しないものと想定しているため、最適化された描画テクニックを使ってSwing GUIのパフォーマンスを向上させます。コンポーネント階層にHWコンポーネントが含まれている場合は、最適化をオフにする必要があります。これにはまずSwingのJScrollPaneが該当します。スクロール・モードを変更するには、JViewPort.setScrollMode(int)メソッドを使用します。

- 不透明でないLWコンポーネント:

HW/LW混在機能の実装ではデフォルトで、不透明でないLWコンポーネントはサポートされません。矩形以外のLWコンポーネントをHWコンポーネントと混在できるようにするには、アプリケーションで非公開APIのcom.sun.awt.AWTUtilities.setComponentMixingCutoutShape()を使用する必要があります。

注意:
矩形でないLWコンポーネントは、引き続き不透明(アルファ= 1.0)または透明(アルファ= 0.0)のいずれかのカラーを使用して自身をペイントするようにしてください。半透明カラー(0.0 < アルファ< 1.0)の使用はサポートされません。

- デフォルトのHW/LW混在機能の無効化:

一部の開発者は以前、HWコンポーネントとLWコンポーネントを混在させる必要がある場合に備えて、独自のサポートを実装しました。JDK 6およびJDK 7以降で使用可能な機能の組込み実装が、カスタムの回避方法との間で問題を発生させる可能性があります。この組込み機能を無効にするには、アプリケーションの起動時に次のシステム・プロパティを指定する必要があります。

-Dsun.awt.disableMixing=true

11 Java 2Dパイプラインのレンダリングおよびプロパティ

この章では、パイプラインのレンダリングおよびプロパティを変更する場合にJava 2D APIで発生する可能性がある、いくつかの最も一般的な問題のトラブルシューティングのための情報およびガイダンスを提供します。

Java 2Dプロパティのサマリーについては、「Java 2Dのプロパティ」を参照してください。

別のパイプラインを選択したりパイプラインのプロパティを操作したりすれば、問題の原因を特定できたり、しばしば回避方法を発見できたりする可能性があります。

通常、構成で使用されるデフォルトのパイプラインを確認することで、Java 2Dパイプラインの問題のトラブルシューティングを行うことができます。次に、パイプラインを別のものに変更するか、デフォルト・パイプラインのプロパティを変更します。

問題が解消すれば、回避方法が見つかったことになります。問題が継続する場合は、別のプロパティを変更するか別のパイプラインに変更してみます。

Java 2Dでは一連のパイプラインが使用されますが、これは大まかに、プリミティブをレンダリングするためのさまざまな方法として定義できます。これらのパイプラインは次のとおりです。

- Oracle SolarisおよびLinux: X11パイプラインがOracle SolarisおよびLinuxオペレーティング・システムのデフォルトです。

- Windows OS - DirectDraw/GDIパイプラインがWindowsのデフォルトです

- Windows OS - 全画面モードのDirect3DパイプラインをWindowsでは代替として選択できます。

- Oracle Solaris、LinuxおよびWindowsでのOpenGLパイプラインは、Oracle SolarisおよびLinuxオペレーティング・システム、ならびにWindowsでの代替手段です。

Oracle SolarisおよびLinux: X11パイプライン

UNIXプラットフォームのデフォルト・パイプラインはX11パイプラインです。このパイプラインでは、画面へのレンダリングや、特定タイプのオフスクリーン・イメージ(VolatileImageなど)または「互換性のある」イメージ(GraphicsConfiguration.createCompatibleImage()メソッドで作成されたイメージ)へのレンダリングを行う際に、Xプロトコルが使用されます。

これらのタイプのイメージは、X11ピックスマップ内に格納すればパフォーマンスを改善できます(特にリモートXサーバーの場合)。

さらに、Java 2Dでは特定の場合に、Xサーバー拡張(MIT X共有メモリー拡張、ダイレクト・グラフィックス・アクセス拡張、BufferStrategy API使用時のダブル・バッファリング用ダブル・バッファ拡張など)が使用されます。

構成によっては、追加パイプラインのOpenGLパイプラインによってパフォーマンスが向上することもあります。

トラブルシューティングの対象となるX11パイプラインのプロパティは次のとおりです。

- X11パイプラインのピックスマップのプロパティ

- X11パイプラインのMIT共有メモリー拡張

- SPARC上のOracle Solaris: DGAのサポート

- SPARC上のOracle Solaris - Java 2Dのデフォルト・ビジュアルの変更

X11パイプラインのピックスマップのプロパティ

Java 2Dではデフォルトで、特定タイプのオフスクリーン・イメージの格納やキャッシングにX11ピックスマップが使用されます。

ピックスマップに格納できるイメージのタイプは、次のものだけです。

- 不透明イメージ(この場合、ColorModel.getTransparency()からTransparency.OPAQUEが返される)

- 1ビット透明イメージ(別名「スプライト」、Transparency.BITMASK)

イメージの格納にピックスマップを使用するメリットは、ドライバの判断でフレーム・バッファのビデオ・メモリー内に配置できる点にあります(そうした場合、これらのピックスマップを画面や別のピックスマップにコピーする際の速度が改善される)。

ピックスマップを使用すれば、通常はパフォーマンスが向上します。ただし、場合によっては逆になることもあります。これらの場合は通常、Xプロトコルを使用して実行できない操作(アンチエイリアス、アルファ合成、単純な移動変換よりも複雑な変換など)が使用されています。

これらの操作については、X11パイプラインは組込みソフトウェア・レンダラを使用してレンダリングを行う必要があります。これにはほとんどの場合、ピックスマップの内容を読み取って(リモートXサーバーの場合はネットワーク経由で)システム・メモリーに格納し、レンダリングを実行した後、それらのピクセルを元のピックスマップに送信するという操作が含まれます。これらの操作ではパフォーマンスが大幅に低下する可能性があります(特にXサーバーがリモートの場合)。

X11パイプラインの使用を無効にする2つの例を次に示します。

- X11パイプラインのピックスマップの無効化:
Java2Dでのピックスマップの使用を無効にするには、Java VMに次のプロパティを渡します。-Dsun.java2d.pmoffscreen=false。

- X11パイプラインの共有メモリー・ピックスマップの無効化:
ピックスマップからのピクセルの読取りを必要とする操作の、全体のパフォーマンスに対する影響を最小限に抑えるため、X11パイプラインは読取り頻度の高いイメージの格納に共有メモリー・ピックスマップを使用します。

注意:
共有メモリー・ピックスマップは、ローカルXサーバーの場合にのみ使用できます。

共有メモリー・ピックスマップを使用するメリットは、X11プロトコルをバイパスしてパイプライン内のピクセルにパイプラインから直接アクセスできる点にあり、その結果、パフォーマンスが向上します。

イメージはデフォルトでは通常のXサーバー・ピックスマップ内に格納されますが、後でそのようなイメージからの過剰な読取りがパイプラインによって検出された場合には、共有メモリー・ピックスマップにイメージを移動できます。イメージのコピー回数が十分な数に達したら、元のサーバー・ピックスマップにイメージを移動できます。

このパイプラインでは、共有メモリー・ピックスマップの使用を制御する方法が2つ用意されています。それらを無効にする方法と、すべてのイメージが共有メモリー・ピックスマップに強制的に格納されるようにする方法です。

まず、共有メモリー・ピックスマップを強制してみます。パフォーマンスが向上することがよくあります。ただし、特定のビデオ・ボード/ドライバ構成では、レンダリング・アーティファクトまたはクラッシュを回避するために共有メモリー・ピックスマップを無効にすることが必要な場合があります。

- 共有メモリー・ピックスマップを無効にするには、J2D_PIXMAPS環境変数をserverに設定します。これが、リモートXサーバーの場合のデフォルトになります。

- すべてのピックスマップが強制的に共有メモリー内に作成されるようにするには、J2D_PIXMAPSをsharedに設定します。

X11パイプラインのMIT共有メモリー拡張

Java 2DのX11パイプラインはMIT共有メモリー拡張(MIT SHM)を使用しますが、この場合、クライアントとXサーバーの間のデータの交換が速くなります。この機能により、Javaアプリケーションのパフォーマンスが大幅に向上する場合があります。

次は、Javaアプリケーションのパフォーマンスを向上させる2つの方法です。

- XサーバーおよびJava 2Dの共有メモリーの増加:

Oracle Solarisオペレーティング・システム・リリース8以前ではときどき、システム(および特にXサーバー)で使用可能な共有メモリーの量を増やす必要がありましたが、これは、デフォルトが低すぎてレンダリングのパフォーマンスが低下することがあったからです。共有メモリーや共有メモリー・セグメントの量を増やすと、パフォーマンスが向上する可能性があります。

Oracle Solarisオペレーティング・システムでデフォルト設定を変更するには、/etc/systemファイルを編集して、shmsys:shminfo_*設定を次の例のように変更します。これは、Oracle Solaris 9以降では不要です。

set shmsys:shminfo_shmmax=10000000
set shmsys:shminfo_shmni=200
set shmsys:shminfo_shminfo=150

Linuxでこの設定を構成するには、/proc/sys/kernel/shm*ファイルを編集します。

- X11パイプラインの共有メモリー拡張の無効化:

古いXサーバーや共有メモリー拡張で問題(クラッシュやレンダリング・アーティファクトなど)が発生する場合、拡張を無効にできると便利です。MIT SHMの使用を無効にするには、J2D_USE_MITSHM環境変数をfalseに設定します。

SPARC上のOracle Solaris: DGAのサポート

SPARCハードウェアでは、フレーム・バッファがSunのダイレクト・グラフィックス・アクセス (DGA) Xサーバー拡張をサポートしていて、フレーム・バッファにアクセスするための対応するモジュールがJava 2Dに含まれている場合、画面へのレンダリングにDGAが使用されます。

オフスクリーン・イメージはすべてJavaヒープ・メモリー内に存在しており、それらへのレンダリングにはJava 2Dのソフトウェア専用レンダリング・パイプラインが使用されます。これは、オフスクリーン・イメージにX11ピックスマップが使用される通常のUNIX構成とは異なっています。

次のユースケースは、DGA拡張サポートを検出し、DGAを無効または有効にする方法を説明します。

- レンダリングのためのDGA拡張

画面へのレンダリング時にDGA拡張が使用されているかどうかを検出するには、なんらかのレンダリングを行うかGUIを表示する任意のJavaアプリケーションを実行し、そのアプリケーション起動時に/tmp/wg*ファイルが作成されたかチェックします。アプリケーションを終了し、ファイルが削除されたことを確認します。その場合、このシステムではJava 2DがDGAを使用しています。

- 一般的なDGAの問題:

DGAではフレーム・バッファのビデオ・メモリーに直接アクセスできるため、典型的な問題として、ウィンドウ境界の外側の破損、完全なシステム、Xサーバー・ロックアップなどが挙げられます。

- DGAの有効化または無効化:

DGAが使用されていることが確認できた場合、最初に試す必要があるのは、その無効化です。これを行うには、NO_J2D_DGA環境変数をtrueに設定します。するとデフォルトのUNIXパスで強制的に、画面へのレンダリングにX11のみが使用され、オフスクリーン・イメージの高速化にピックスマップが使用されるようになります。

ときには、画面へのレンダリングにDGAを使用し、ピックスマップの使用を有効にすることが有益な場合もあります。オフスクリーン・イメージの高速化のためにピックスマップの使用を強制するには、アプリケーションの起動時に-Dsun.java2d.pmoffscreen=trueプロパティを設定します。

SPARC上のOracle Solaris - Java 2Dのデフォルト・ビジュアルの変更

SPARCプラットフォームの特定のビデオ・ボードでは、複数のビジュアルがXサーバーから使用できます。

Java 2Dはデフォルトで最適なビジュアルを選択しようとしますが、この「最適」というのは通常、ビットの深さがより深いビジュアルを指します。たとえば一部のOracle Solarisオペレーティング・システム・リリースでは、デフォルトX11ビジュアルは8ビット疑似カラーですが、24ビットのビジュアルも使用できます。これらの場合、Java 2Dは、Javaウィンドウのデフォルトとして24ビット・トゥルーカラー・ビジュアルを選択します。

別のビジュアルに対応するGraphicsConfigurationオブジェクトでJavaトップレベル・ウィンドウを作成することも可能ですが、場合によっては、かわりに別のデフォルト・ビジュアルをJavaに使用させる必要が生じることがあります。これを行うには、FORCEDEFVIS環境変数を設定します。これは、trueに設定してデフォルトXサーバー・ビジュアルの使用を(たとえ最適でなくても)強制することができる一方で、xdpyinfoなどのツールで報告されたビジュアルIDに対応する16進数に設定することもできます。

Xサーバーのデフォルト・ビジュアルを確認するには、xdpyinfoコマンドを実行し、default visual idのフィールドを確認します。

Windows OS - DirectDraw/GDIパイプライン

Windowsプラットフォームのデフォルト・パイプラインは、DirectDrawパイプラインとGDIパイプラインを組み合わせたものであり、DirectDrawパイプラインで実行される操作もあれば、GDIパイプラインで実行される操作もあります。高速化されたオフスクリーンやオンスクリーン表面へのレンダリングには、DirectDrawとGDIのAPIが使用されます。

Java SE 6リリース以降では、ドライバが要件を満たしていれば、アプリケーションがフル・スクリーン・モードに入るときに新しいDirect3Dパイプラインを使用できます。Direct3Dパイプラインの問題としては、レンダリング・アーティファクト、クラッシュ、パフォーマンス関連の問題などが考えられます。

構成によっては、追加パイプラインのOpenGLパイプラインによってパフォーマンスが向上することもあります。

次は、レンダリング・アーティファクト、クラッシュ、パフォーマンス関連の問題など、Direct3Dパイプラインの問題のトラブルシューティングを行う3つの例です。

- DirectDrawパイプラインの無効化:

DirectDrawが無効になっていると、すべての操作がGDIで行われます。DirectDrawの使用を無効にするには、-Dsun.java2d.noddraw=trueフラグを指定します。この場合、すべてのオフスクリーン・イメージがJavaヒープ内で作成され、それらへのレンダリングにはデフォルトのソフトウェア・パイプラインが使用されます。画面へのオフスクリーン・イメージのコピーだけでなく、オンスクリーンのすべてのレンダリングにもGDIが使用されます。

- DirectDrawパイプラインの有効化:

なんらかの理由でパイプラインがデフォルトで無効化された場合にそれを有効にするには、-Dsun.java2d.noddraw=falseフラグをVMに指定します。

ただし通常は、そもそも無効にされた理由が存在するので、強制しないほうが得策です。

- 組込みパント・メカニズムの無効化:

一般に、DirectDrawパイプラインはオフスクリーン表面をフレーム・バッファのビデオ・メモリー内に配置しようとしますが、これにより、それらの表面を画面や他の高速化された表面にコピーする操作が高速化される以外に、特定のグラフィックス操作のハードウェア高速化レンダリングも可能となります。

VRAMベースの表面への非高速化レンダリングの影響を抑えるために、パント・メカニズムが存在します。このメカニズムでは、頻繁に読み取られると判明した表面をシステム・メモリーに移動します。頻繁にコピーされると判明した表面は、ビデオ・メモリーに戻すよう促されることがあります。

ただし、パイプラインがDirectDraw APIを使用して実行できない操作(アルファ合成、変換、アンチエイリアスなどを使用する操作)については、ソフトウェア・パイプラインを使用してレンダリングが実行されます。これは場合によっては、VRAM内に存在する宛先表面のピクセルを読み取ってシステム・メモリーに格納する必要があることを意味しますが、それは非常にコストの高い操作です。

特定のビデオ・ボード/ドライバの組合せでは、システム・メモリー・ベースのDirectDraw表面がレンダリング・アーティファクトやその他の問題の原因となることが知られています。DirectDrawパイプラインには、システム・メモリー表面が使用されないようにパント・メカニズムを無効にするための方法が用意されています。

組込み表面パント・メカニズムを無効にするには、Java VMに-Dsun.java2d.ddforcevram=trueフラグを指定します。

注意:

このメカニズムにより、パフォーマンスが低下することがあります。操作のたびにソフトウェア・ループによってVRAMからピクセルが読み取られる可能性があるためです。この場合は、DirectDrawパイプラインの無効化を検討します。

- DirectDraw BILT操作の無効化:

Bit Block Transfer (BILT)操作では、2つのビットマップ・パターンが合成されます。この操作は、Graphics.drawImage() APIの呼出しに対応します。

場合によっては、DirectDraw BLIT操作を無効にすることでレンダリングの問題を回避できます。かわりにGDI BLITが使用されます。

注意:
この操作によってパフォーマンスが低下する場合があります。代わりにDirectDrawパイプラインの無効化を検討してください。

DirectDraw BLIT操作の使用を無効にするには、パラメータ-Dsun.java2d.ddblit=falseをJava VMに渡します。

Windows OS - 全画面モードでのDirect3Dパイプライン

Java SE 6リリース以降のDirect3Dパイプラインでは、レンダリングにDirect3D APIが使用されます。フル・スクリーン・モードではこのパイプラインがデフォルトで有効にされます(ドライバが、必要な機能とそのレンダリング品質レベルをサポートしている場合)。

次の項で説明するように、Direct3Dパイプラインを有効にし、その使用を強制できます。

アルファ合成、アンチエイリアス、変換などのレンダリング操作を大量に使用するアプリケーションでは、Direct3Dパイプラインを有効にすることを検討してください。

ただし、このパイプラインをアプリケーションで有効にすることを決定する際には注意してください。たとえば、一部の組み込みビデオ・チップセット(大部分のノートブックで使用されているもの)は、たとえJava 2Dパイプラインの品質要件を満たしていても、Direct3D使用時には良好なパフォーマンスを示しません。

Direct3D APIの問題のトラブルシューティングを行う3つの例を次に示します。

1. Direct3Dパイプラインの無効化:

一部の古いビデオ・ボード/ドライバの組み合わせでは、Direct3Dパイプラインで問題(レンダリングとパフォーマンスの両方)が発生することが知られています。これらの場合にJava SE 5以降のリリースでパイプラインを無効にするには、パラメータ-Dsun.java2d.d3d=falseをJava VMに渡すか、J2D_D3D環境変数をfalseに設定します。

2. Direct3Dパイプラインの有効化:

Java SE 5以降のリリースで、ウィンドウ・モードとフル・スクリーン・モードの両方でDirect3Dパイプラインを有効にするには、パラメータ-Dsun.java2d.d3d=trueを使用するか、J2D_D3D環境変数をtrueに設定します。

注意:
パイプラインは、ドライバが最小限必要な機能をサポートしている場合にのみ有効になります。

3. Direct3Dパイプラインのレンダリング問題の診断:

Java SE 8リリースでは、様々なDirect3Dラスタライザを強制することで、いくつかのレンダリングの問題(ピクセルの欠落や不明瞭なレンダリングなど)を診断できます。J2D_D3D_RASTERIZER環境変数をref、rgb、hal、またはtnlのいずれかに設定します。

これらのラスタライザの詳細は、Direct3Dのドキュメントを参照してください。デフォルトでは、通知された機能に基づいて最適なラスタライザが選択されます。具体的には、refラスタライザでは、Microsoft製のリファレンスDirect3Dラスタライザの使用が強制されます。このラスタライザでレンダリングの問題が再現できない場合、その問題はビデオ・ドライバのバグの可能性が高いと考えられます。

rgbラスタライザが使用可能なのは、Direct3D SDKがインストールされている場合だけです。このSDKはMicrosoft Game Technologies Centerから取得できます。

Direct3Dパイプラインのテキスト・レンダリングでパフォーマンスや品質の問題が発生する場合、Direct3Dパイプラインのグリフ・キャッシュで、デフォルトのアルファ・テクスチャのかわりにARGBテクスチャを使用することを強制できます。これを行うには、J2D_D3D_NOALPHATEXTURE環境変数をtrueに設定します。

Oracle Solaris、LinuxおよびWindowsでのOpenGLパイプライン

OpenGLパイプラインは、Oracle Solaris、LinuxおよびWindowsで使用できます。

この代替パイプラインでは、VolatileImage、BufferStrategy APIで作成されたバック・バッファ、および画面へのレンダリング時に、ハードウェア高速化されたクロス・プラットフォームのOpenGL APIが使用されます。

このパイプラインはデフォルト(X11またはGDI/DirectDraw)のパイプラインに比べ、特定のアプリケーションでパフォーマンス上の大きな利点を提供できます。アルファ合成、アンチエイリアス、変換などのレンダリング操作を大量に使用するアプリケーションでは、このパイプラインを有効にすることを検討してください。

OpenGLパイプラインの問題のトラブルシューティングを行うユースケースを次に示します

- OpenGLパイプラインの有効化

- 最小要件

- 起動の問題の診断

- レンダリングおよびパフォーマンスの問題の診断

- 最新のOpenGLドライバ

OpenGLパイプラインの有効化

OpenGLパイプラインはデフォルトで無効になっています。

OpenGLパイプラインの有効化を試みるには、次のオプションをJVMに指定します。

-Dsun.java2d.opengl=true

OpenGLパイプラインが特定のスクリーンに対して正常に初期化されたかどうかに関する、詳細なコンソール出力を受け取るには、オプションをTrueに設定します(大文字「T」に注意)。

最小要件

ハードウェアまたはドライバが最小要件を満たしていなければ、OpenGLパイプラインは有効にされません。

次の要件のいずれかが満たされない場合、Java 2Dはフォール・バックしてデフォルト・パイプライン(Oracle Solaris/LinuxのX11またはWindowsのGDI/DirectDraw)を使用するため、アプリケーションは正しく動作し続けますが、OpenGLの高速化は失われます。

Oracle SolarisおよびLinuxオペレーティング・システムの最低要件を次に示します。

- ハードウェア高速化のOpenGL/GLXライブラリがインストールされ、適切に構成されている

- OpenGLのバージョンが1.2以降

- GLXのバージョンが1.3以降

- 使用可能な深度バッファ付きの、少なくとも1つのトゥルーカラー・ビジュアル

Windows OSの最小要件は、次のとおりです。

- 拡張WGL_ARB_pbuffer、WGL_ARB_render_texture、およびWGL_ARB_pixel_formatをサポートするハードウェア高速化ドライバ

- OpenGLのバージョンが1.2以降

- 使用可能な深度バッファ付きの、少なくとも1つのピクセル形式

起動の問題の診断

OpenGLベースのJava 2Dパイプラインの起動手順に関する詳細情報を取得するには、J2D_TRACE_LEVEL環境変数を使用します。

前述したように、特定のマシン上で様々な理由でOpenGLパイプラインが有効にされない場合があります。たとえば、ドライバが正しくインストールされていない可能性や、報告されたバージョン番号が不十分である可能性があります。あるいは、マシンに搭載されている古いグラフィックス・カードがOpenGLの適切なバージョンや拡張をサポートしていない可能性もあります。

Java SE 6以降のリリースでは、次の例に示すように、J2D_TRACE_LEVEL環境変数を使用してOpenGLベースのJava 2Dパイプラインの起動手順に関する詳細情報を取得できます。

Windowsでは、J2D_TRACE_LEVEL環境変数を設定します。

# set J2D_TRACE_LEVEL=4
# java -Dsun.java2d.opengl=True YourApp

SolarisおよびLinuxでは、J2D_TRACE_LEVEL環境変数を設定します。

# export J2D_TRACE_LEVEL=4
# java -Dsun.java2d.opengl=True YourApp

その出力は、プラットフォームや取り付けられたグラフィックス・ハードウェアに応じて異なりますが、OpenGLパイプラインがユーザーの構成で正常に有効にされない理由に関する何らかの洞察を提供してくれます。

注意:

この出力は特に、SunのJava 2Dチーム宛のバグ・レポートを記入する際に特に役立ちます。

レンダリングおよびパフォーマンスの問題の診断

レンダリングまたはパフォーマンスの問題の原因がJava 2DまたはOpenGLドライバであるかどうかを診断します。

OpenGLパイプラインはベースとなるグラフィックス・ハードウェアやドライバに非常に強く依存しているため、レンダリングやパフォーマンスの問題の原因がJava 2D、OpenGLドライバのどちらなのかを判定しかねる場合があります。

Java SE 6リリースでのOpenGLパイプラインの新機能の1つは、VolatileImage使用時のレンダリング・パフォーマンスの改善とVRAM消費量の低減を図るGL_EXT_framebuffer_object拡張の使用です。この「FBO」コード・パスは、OpenGLパイプラインが有効なときにデフォルトで有効になりますが、それは、グラフィックス・ハードウェアとドライバがこのOpenGL拡張をサポートしている場合に限られます。この拡張は一般に、Nvidia GeForce/Quadro FXシリーズ以降およびATI Radeon 9500以降で使用できます。「FBO」コード・パスがアプリケーションの問題の原因となっている疑いがある場合、次のシステム・プロパティを設定してそれを無効にできます。

-Dsun.java2d.opengl.fbobject=false

このプロパティが設定されると、Java 2Dは古いpbuffer-based コード・パスにフォール・バックします。

特定のJava 2D操作で得られる視覚的な結果が、OpenGLパイプラインを有効にしたときとしなかったときで異なる場合、それはおそらく、グラフィックス・ドライバのバグを示しています。同様に、OpenGLパイプラインを有効にしたときに、しなかった場合よりもJava 2Dレンダリングのパフォーマンスが大幅に悪化する場合、その原因はおそらくドライバまたはハードウェアの問題です。

どちらの場合も、通常のバグ報告チャネルを通じて詳細なバグ・レポートを提出してください。「バグ・レポートの提出」を参照してください。バグ・レポートの提出時にはできるだけ詳しく記述し、次の情報を含めてください。

- オペレーティング・システム(Ubuntu Linux 6.06、Windows XP SP2など)

- グラフィックス・ハードウェアの製造元とデバイスの名前(Nvidia GeForce 2 MX 440など)

- ドライバの正確なバージョン(ATI Catalyst 6.8、Nvidia 91.33など)

- J2D_TRACE_LEVEL=4をコマンド行に指定したときの出力(前の項で説明)

- Oracle SolarisまたはLinuxを使用している場合は、glxinfoコマンドの出力

最新のOpenGLドライバ

グラフィックス・カードの製造元とそれに対応するWebサイト、サポートされているプラットフォーム、およびカードのいくつかの例のリスト。

OpenGLパイプラインはOpenGL APIおよびベースとなるグラフィックス・ハードウェアやドライバに強く依存しているため、最新のグラフィックス・ドライバがマシンにインストールされているのを確認することが非常に重要です。ドライバは、グラフィックス・カードの製造元のWebサイトからダウンロードできます(次の表を参照)。

製造元

プラットフォーム

動作確認済みのカード

ATI

Linux、Windows

Radeon 8500以降、FireGLシリーズ

Nvidia

Oracle Solaris on x64、LinuxおよびWindows

GeForce 2シリーズ以降、Quadro FXシリーズ以降

Oracle

Oracle Solaris on SPARC

Expert3Dシリーズ、XVR-500、XVR-600、XVR-1200、XVR-2500

Xi Graphics

Oracle Solaris on x86、Linux

各種(Xi Graphicsで確認)

12 Java 2D

Java 2D APIで発生する可能性のある一般的な問題のいくつかをトラブルシューティングするための情報および指針。

この章の構成は、次のとおりです。

- 一般的なパフォーマンス問題

- テキスト関連の問題

- Java 2D印刷

Java 2Dプロパティのサマリーについては、「Java 2Dのプロパティ」を参照してください。

一般的なパフォーマンス問題

Java 2Dハードウェア高速化レンダリング・プリミティブに関する一般的なパフォーマンス問題、およびプリミティブ・トレースを検出して非高速化レンダリングを回避する方法。

レンダリング・パフォーマンスの低下には様々な原因が考えられます。次のトピックでは、アプリケーションのレンダリング・パフォーマンスの低下の原因を特定し、ソフトウェアのみのレンダリング・パフォーマンスを改善するためのアプローチを提案します。

このトピックでは、次の項目について説明します。

- ハードウェア高速化レンダリング・プリミティブ

- 非高速化レンダリングの検出および回避のためのプリミティブ・トレース

- 低いレンダリング・パフォーマンスの原因

- ソフトウェアのみのレンダリングのパフォーマンスの改善

ハードウェア高速化レンダリング・プリミティブ

パフォーマンス問題の原因についての理解を深めるため、ハードウェア高速化の意味について考えてます。

一般に、ハードウェア高速化レンダリングは2つのカテゴリに分けられます。

- 「高速化対象」の宛先へのハードウェア高速化レンダリング。ハードウェア高速化が可能なレンダリングの宛先の例として、VolatileImage、画面、およびBufferStrategyがあげられます。宛先が高速化対象である場合、表面に対するレンダリングはビデオ・ハードウェアによって実行される可能性があります。したがって、ユーザーがdrawRect呼出しを発行すると、Java 2Dはその呼出しを、ベースとなるネイティブAPI (GDI、DirectDraw、Direct3D、OpenGL、X11など)にリダイレクトし、そこでハードウェアを使用して操作が実行されます。

- 高速化メモリー(ビデオ・メモリーまたはピックスマップ)へのイメージのキャッシング。これにより、それらのイメージを別の高速化対象表面に非常に高速でコピーできるようになります。これらのイメージは管理対象イメージと呼ばれます。

理想的には、高速化対象表面で実行される操作はすべて、ハードウェアで高速化されます。この場合、アプリケーションはプラットフォームによって提供されるメリットを最大限に享受できます。

残念ながら多くの場合、デフォルト・パイプラインはレンダリングにハードウェアを使用できません。その原因は、パイプラインの制限やベースとなるネイティブAPIにあります。たとえば、ほとんどのXサーバーは、アンチエイリアス・プリミティブのレンダリングやアルファ合成をサポートしません。

パフォーマンス問題の原因の1つは、実行される操作がハードウェア高速化されない場合にあります。宛先表面が高速化される場合でも、その一部のプリミティブが高速化されない可能性があります。

ハードウェア高速化が使用されていない場合を検出する方法を知ることが重要です。それを知れば、パフォーマンスの改善が容易になる可能性があります。

非高速化レンダリングの検出および回避のためのプリミティブ・トレース

高速化されないレンダリングを検出するには、Java 2Dプリミティブ・トレースを使用できます。

Java 2Dには組込みのプリミティブ・トレースが含まれています。

アプリケーションの実行時に-Dsun.java2d.trace=countを指定します。アプリケーションが終了すると、プリミティブのリストとそれらのカウントがコンソールに出力されます。

MaskBlitプリミティブや任意のGeneral*プリミティブが表示された場合、それは通常、レンダリングの一部がソフトウェア・ループによって処理されていることを意味します。Linux上で、半透明のBufferedImageに対してdrawImageを実行してVolatileImageに書き込む場合の出力を、次に示します。

sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, "Integer BGR Pixmap")sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntBgr)

デフォルト・パイプラインでの一般的な非高速化プリミティブと、トレース出力に示されるそれらの特徴をいくつかあげてみます。

注意:
このトレースのほとんどはLinuxで行われたものです。使用しているプラットフォームや構成によっては多少の違いが生じる可能性があります。

- 半透明イメージ(ColorModel.getTranslucency()でTranslucency.TRANSLUCENTが返されるイメージ)、またはAlphaCompositingを使用したイメージ。サンプルのプリミティブ・トレース出力:

sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb,SrcOverNoEa, "Integer BGR Pixmap")sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntBgr)

- アンチエイリアスの使用(アンチエイリアス・ヒントの設定による)。サンプルのプリミティブ・トレース出力:

sun.java2d.loops.MaskFill::MaskFill(AnyColor, Src, IntBgr)

- アンチエイリアス・テキストのレンダリング(テキスト・アンチエイリアス・ヒントの設定)。サンプル出力は次のいずれかになります。

- sun.java2d.loops.DrawGlyphListAA::DrawGlyphListAA(OpaqueColor, SrcNoEa, AnyInt)

- sun.java2d.loops.DrawGlyphListLCD::DrawGlyphListLCD(AnyColor, SrcNoEa, IntBgr)

- アルファ合成(半透明カラー(0xff以外のアルファ値を含むカラー)でのレンダリング、Graphics2D.setComposite()によるデフォルト以外のAlphaCompositingモードの設定、のいずれかによる)。

sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOver, IntRgb)sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb)
]

- 本格的な変換(単なる移動ではない変換)。変換された不透明イメージのVolatileImageへのレンダリング。

sun.java2d.loops.TransformHelper::TransformHelper(IntBgr, SrcNoEa, IntArgbPre)

- 回転されるラインのレンダリング。

sun.java2d.loops.DrawPath::DrawPath(AnyColor, SrcNoEa, AnyInt)

アプリケーションをトレース付きで実行し、不要な非高速化プリミティブを使用していないことを確認してください。

低いレンダリング・パフォーマンスの原因

レンダリング・パフォーマンス悪化の原因および可能な代替方法のリスト。

いくつかのレンダリング・パフォーマンスの低下の原因および可能な選択肢を次に示します。

- 高速化レンダリングと非高速化レンダリングの混在:

特定のパイプラインでの高速化表面へのレンダリング時に、アプリケーションによってレンダリングされるプリミティブの一部しか高速化できない状況では、スラッシングが発生する可能性があります。パイプラインで常にレンダリング・パフォーマンスの改善に向けた調整が試行されても、成功する可能性がおそらくほとんどないからです。

レンダリング・プリミティブの大部分が高速化されないことが事前にわかっている場合、BufferedImageにレンダリングしてからバックバッファや画面にコピーするか、あるいは説明したフラグのいずれかを使用して非ハードウェア高速化パイプラインに切り替えたほうが得策です。

注意:

このアプローチを採用した場合、Java 2Dでのハードウェア高速化の使用状況が将来改善されても、そのメリットをアプリケーションで享受できなくなる可能性があります。

たとえば、リモートXサーバーの場合に使用頻度の高いアプリケーション内でアンチエイリアスやアルファ合成などが多用されると、パフォーマンスが極度に低下する可能性があります。これを避けるには、-Dsun.java2d.pmoffscreen=falseプロパティをJavaランタイムに渡すかプログラム内でSystem.setProperty() APIを使用して設定して、ピックスマップの使用を無効にします。

注意:
このプロパティは1回のみ読み込まれるので、GUI関連の操作の前に設定する必要があります。

- 最適でないレンダリング・プリミティブ:

目的の視覚効果を実現する際に、可能なかぎりもっとも単純なプリミティブを使用することをお薦めします。

たとえば、Graphics.drawLine()を使用して新しいLine2D().draw()は使用しません。結果は同じようにみえます。ただし、2番目の操作では、通常レンダリング・コストの非常に高い汎用形状としてレンダリングされるため、計算量が格段に多くなります。形状は、プリミティブ・トレース内で、アンチエイリアス設定や特定のパイプラインに応じてさまざまな方法で表示されますが、おそらく多数の*FillSpansまたはDrawPathプリミティブとして表示されます。

複雑な属性のもう1つの例は、GradientPaintです。これは、デフォルト以外の一部のパイプライン(OpenGLなど)ではハードウェア高速化される可能性がありますが、デフォルト・パイプラインではハードウェア高速化されません。したがって、パフォーマンスの問題が発生する場合にGradientPaintの使用を制限できます。

- ヒープに基づく出力先のBufferedImage:

BufferedImageへのレンダリングでは、ほとんど常にソフトウェア・ループが使用されます。

一部のSPARCシステムでの例外は、特定のイメージング操作を高速化するためにVIS命令セットが使用される場合があることです。VIS命令セットを参照してください。

レンダリングが高速化される機会を残すには、BufferStrategyまたはVolatileImageオブジェクトをレンダリング先として選択してください。

- 組込み高速化メカニズムの無効化:

Java 2Dは特定タイプのイメージを高速化しようとします。VolatileImageなどの高速化宛先へのコピーを高速化するため、イメージの内容がビデオ・メモリーにキャッシュされる可能性があります。これらのメカニズムは、意図せずにアプリケーションで無効化される場合があります。

- getDataBuffer()によるピクセルへの直接アクセス:

アプリケーションがgetRaster().getDataBuffer() APIを使用してBufferedImageピクセルにアクセスした場合、Java 2Dは、キャッシュ内のデータが最新であることを保証できないため、このタイプのイメージの高速化の試みをすべて無効にします。

これを回避するには、getDataBuffer()を呼び出さないでください。代わりに、BufferedImage.getRaster()メソッドで取得可能なWriteableRasterを操作します。

ピクセルを直接変更する必要がない場合には、イメージをビデオ・メモリー内に手動でキャッシュするためにイメージのキャッシュ・コピーをVolatileImage内に保持し、元のイメージが更新された時点でそのキャッシュ・データを更新します。

- すべてのコピーの前のスプライト・レンダリング:

アプリケーションがイメージを高速化表面(VolatileImage、BufferStrategy)にコピーする前にイメージにレンダリングする場合、そのイメージは、高速化メモリーにキャッシュされるメリットを享受できません。その理由は、元のイメージが更新されるたびにキャッシュ・コピーを更新する必要があり、そのためにデフォルトのシステムメモリーベースの表面だけが使用されて、高速化されないからです。

- 高速化メモリー・リソースの枯渇:

アプリケーションが多数のイメージを使用すると、使用可能な高速化メモリーが使い果たされる可能性があります。これがアプリケーションのパフォーマンス問題の原因となっている場合、リソースを管理しなければいけない可能性があります。

次のAPIを使えば、使用可能な高速化メモリーの量を要求できます。GraphicsDevice.getAvailableAcceleratedMemory()。

さらに次のAPIを使えば、イメージが高速化されているかどうかを判定できます。Image.getCapabilities()。

アプリケーションがリソースを使い果たしていると判断した場合は、必要のないイメージを保持しないことにより、問題を処理できます。たとえば、ゲームが次のレベルに進んだら、以前のレベルに含まれるイメージをすべて解放します。また、イメージに関連付けられた高速化リソースをImage.flush() APIで解放することもできます。

高速化優先度API Image.getAccelerationPriority()およびsetAccelerationPriority()を使用してイメージの高速化優先度を指定することもできます。少なくともバック・バッファを高速化することは妥当なので、これを最初に作成し、高速化優先度1 (デフォルト)を指定します。また、必要であれば、高速化優先度を0.0に設定して、特定のイメージが高速化されないようにすることもできます。

ソフトウェアのみのレンダリングのパフォーマンスの改善

ソフトウェアのみのレンダリングのパフォーマンスの改善方法。

アプリケーションが(BufferedImageへのレンダリングのみを行うかデフォルト・パイプラインを非高速化パイプラインに変更したために)ソフトウェア専用レンダリングに依存している場合、あるいはアプリケーションが混在されたレンダリングを行う場合でも、次に示す特定のアプローチによってパフォーマンスが改善します。

1. 最適化されたサポートとイメージの種類または操作:
プラットフォーム全体のサイズ制約のために、あるイメージ形式を別の形式に変換するための最適化されたルーチンは、Java 2Dには限られた数しか含まれていません。最適化された直接ループが見つからない状況では、Java 2Dは中間イメージ形式(IntArgb)を介して変換します。この場合、パフォーマンスが低下します。

Java 2Dプリミティブ・トレースを使用すればそのような状況を検出できます。

各drawImage呼出しに対して2つのプリミティブが存在します。1つ目はイメージをソース形式から中間IntArgb形式に変換し、2つ目は中間IntArgbから変換先の形式に変換します。

そのような状況を回避する2つの方法を次に示します。

- 可能であれば別のイメージ形式を使用します。

- イメージを、よりサポート・レベルの高い形式(INT_RGBやINT_ARGBなど)の中間イメージに変換します。そうすれば、カスタム・イメージ形式からの変換が、コピーごとに発生する代わりに一度だけ発生するようになります。

2. 透明と半透明:
できれば、完全な半透明(INT_ARGBなど)のイメージではなく、1ビット透明(BITMASK)のイメージをスプライトとして使用することを検討してください。

完全なアルファを持つイメージの処理では、CPUの負荷がより高くなります。

1ビット透明イメージを取得するには、GraphicsConfiguration.createCompatibleImage(w,h, Transparency.BITMASK)の呼出しを使用します。

テキスト関連の問題

テキストのレンダリングに関連して発生する可能性がある問題とクラッシュ、およびそのような問題を克服するためのヒントの説明。

この項には次のサブセクションが含まれます:

- テキスト・レンダリング中のアプリケーション・クラッシュ

- テキストの外観の違い

- メトリック

テキスト・レンダリング中のアプリケーション・クラッシュ

テキスト・レンダリング中にアプリケーションがクラッシュする場合は、まず致命的エラー・ログ・ファイルをチェックしてください。

このエラー・ログ・ファイルの詳細は、「致命的エラー・ログ」を参照してください。fontmanager.dll内でクラッシュが発生した場合やスタック内にfontmanagerが存在している場合は、フォント処理コード内でクラッシュが発生したことになります。次の例は、一般的なネイティブ・スタック・フレーム(完全なログ・ファイルからの抜粋)を示しています。

Stack: [0x008a0000,0x008f0000),  sp=0x008ef52c,  free space=317k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [ntdll.dll+0x1888f]
C  [ntdll.dll+0x18238]
C  [ntdll.dll+0x11c76]
C  [MSVCR71.dll+0x16b3]
C  [MSVCR71.dll+0x16db]
C  [fontmanager.dll+0x21f9a]
C  [fontmanager.dll+0x22876]
C  [fontmanager.dll+0x1de40]
C  [fontmanager.dll+0x1da94]
C  [fontmanager.dll+0x48abb]
j  sun.font.FileFont.getGlyphImage(JI)J+0
j  sun.font.FileFontStrike.getGlyphImagePtrs([I[JI)V+92
j  sun.font.GlyphList.mapChars(Lsun/java2d/loops/FontInfo;I)Z+37
j  sun.font.GlyphList.setFromString(Lsun/java2d/loops/FontInfo;Ljava/lang/String;FF)Z+71
j  sun.java2d.pipe.GlyphListPipe.drawString(Lsun/java2d/SunGraphics2D;Ljava/lang/String;DD)V+148
j  sun.java2d.SunGraphics2D.drawString(Ljava/lang/String;II)V+60
j  FontCrasher.tryFont(Ljava/lang/String;)V+138
j  FontCrasher.main([Ljava/lang/String;)V+20
v  ~StubRoutines::call_stub

この場合、特定のフォントがおそらく問題です。その場合は、このフォントをシステムから削除すれば、おそらく問題が解決します。

フォント・ファイルを特定するには、アプリケーションの実行時に-Dsun.java2d.debugfonts=trueを指定します。次の例に示すように、通常、最後に示されたフォントが、問題の原因となっているフォントです。

INFO: Registered file C:\WINDOWS\Fonts\WINGDING.TTF as font ** TrueType Font: Family=Wingdings
Name=Wingdings style=0 fileName=C:\WINDOWS\Fonts\WINGDING.TTF rank=2
Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont
INFO: Opening deferred font file SYMBOL.TTF
Aug 16, 2006 10:59:06 PM sun.font.FontManager addToFontList
INFO: Add to Family Symbol, Font Symbol rank=2
Aug 16, 2006 10:59:06 PM sun.font.FontManager registerFontFile
INFO: Registered file C:\WINDOWS\Fonts\SYMBOL.TTF as font ** TrueType Font: Family=Symbol
Name=Symbol style=0 fileName=C:\WINDOWS\Fonts\SYMBOL.TTF rank=2
Aug 16, 2006 10:59:06 PM sun.font.FontManager findFont2D
INFO: Search for font: Dialog
Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont
INFO: Opening deferred font file ARIALBD.TTF
Aug 16, 2006 10:59:06 PM sun.font.FontManager addToFontList
INFO: Add to Family Arial, Font Arial Bold rank=2
Aug 16, 2006 10:59:06 PM sun.font.FontManager registerFontFile
INFO: Registered file C:\WINDOWS\Fonts\ARIALBD.TTF as font ** TrueType Font: Family=Arial
Name=Arial Bold style=1 fileName=C:\WINDOWS\Fonts\ARIALBD.TTF rank=2
Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont
INFO: Opening deferred font file WINGDING.TTF
Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont
INFO: Opening deferred font file SYMBOL.TTF
Aug 16, 2006 10:59:06 PM sun.font.FontManager findFont2D
INFO: Search for font: Dialog
Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont
INFO: Opening deferred font file ARIAL.TTF
Aug 16, 2006 10:59:06 PM sun.font.FontManager addToFontList
INFO: Add to Family Arial, Font Arial rank=2
Aug 16, 2006 10:59:06 PM sun.font.FontManager registerFontFile
INFO: Registered file C:\WINDOWS\Fonts\ARIAL.TTF as font ** TrueType Font: Family=Arial
Name=Arial style=0 fileName=C:\WINDOWS\Fonts\ARIAL.TTF rank=2
Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont
INFO: Opening deferred font file WINGDING.TTF
Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont
INFO: Opening deferred font file SYMBOL.TTF

注意:

場合によっては、最後に言及されたフォントが問題ではない可能性があります。フォント名が出力されるのは初回使用時であり、その後の使用では表示されません。

この特定のフォントが問題の原因であることを確認するには、そのフォントをシステムから一時的に削除します。この特定のファミリ名に関連付けられたファイル名は、出力から容易にわかります。

もう1つの確認方法は、Font2DTestツール(demo/jfc/Font2DTest)を使用して疑わしいフォントをテストすることです。特定のフォントのサイズ、スタイル、およびラスター化モードを指定できます。Font2DTestで特定のフォントを表示させる途中でJDKがクラッシュした場合、そのフォントが、問題の原因となったフォントである可能性が非常に高くなります。

あるフォントがJDKのクラッシュを引き起こしたことがわかった場合、特定のフォントとオペレーティング・システムを含めて、この問題をバグ・データベースに報告することは非常に重要です。「バグ・レポートの提出」を参照してください。

テキストの外観の違い

Javaは独自のフォント・ラスタライザを備えているため、Javaアプリケーションとネイティブ・アプリケーションとでテキストの外観に何らかの小さな違いがあることが予想できます。

こうした相違点の一般的な原因の1つは、アンチエイリアス設定が違う可能性があることです。特に、Swingアプリケーションは、Linuxデスクトップのフォント・アンチエイリアス設定を無視する場合があります。

この動作には、考えられる理由がいくつかあります。

- リモートX11経由の場合、パフォーマンス上の理由により、アンチエイリアスがデフォルトでは有効になりません。Java 2D FAQのフォントおよびテストに関する質問に関する項を参照してください。

- 埋込みビットマップを使用するCJKフォントは、サブピクセル・テキストの代わりにビットマップを使ってレンダリングされる可能性があります。

- サポートされていないデスクトップの中には、そのフォント・スムージング設定が適切に報告されないものがあります。たとえば、KDEはサポートされていませんが、たいていは動作するはずです。しかし、なんらかの問題によってJDKはその設定を検出できないようです。

構成が予想どおりのものであることを確認する最良の方法は、Font2DTestを実行し、ネイティブ・アプリケーションで使用されているフォントを明示的に選択し、ほかのパラメータを必要に応じて設定することです。図12-1は、Font2DTestツールのサンプル画面です。

図12-1 Font2DTestツールのサンプル画面

ヒント:

ユーザー独自の文字列を入力するには、Text to useというラベルの付いたドロップ・ダウン・リストで「User Text」を選択します。

Java言語のフォントのサイズは常に72 dpiで表現されます。ネイティブOSでは別の画面dpiを使用できるため、調整が必要になります。対応するJavaフォント・サイズを計算するには、Toolkit.getScreenResolution()を72で割り、その結果にネイティブ・フォントのサイズを掛けます。

Windowsルック・アンド・フィールや(Oracle SolarisおよびLinuxオペレーティング・システム用の) GTKルック・アンド・フィールなど、すべてのネイティブSwingルック・アンド・フィールで、Swingコンポーネントによってこの調整が自動的に実行されますが、Font2DTestを実行する場合、そのテキスト表示領域では常に72 dpiが使用されます。

Windows以外のオペレーティング・システムでは通常、Type1フォントではなくTrueTypeフォントの使用をお薦めします。フォント・タイプを知るもっとも簡単な方法はファイル拡張子を調べることです。pfaおよびpfbという拡張子はType1フォントを表し、ttf、ttc、およびtteという拡張子はTrueTypeフォントを表します。

メトリック

テキストの境界が予想していたものと異なることに気づいた場合、境界の適切な計算方法を使用していることを確認してください。たとえば、FontMetricsから得られる高さは特定のテキストに固有のものではなく、stringWidthは論理的な有効幅を示し、幅と同じものではありません。詳細は、Java 2D FAQのフォントおよびテキストに関する質問を参照してください。

Java 2D印刷

Java 2D印刷で発生する可能性がある問題のリスト。

このセクションでは、Java 2D印刷で発生する可能性のあるいくつかの問題を説明し、その原因と解決方法を提示します。

Java 2D FAQの印刷に関する質問も参照してください。

1. Windowsでの印刷中にJREがクラッシュする。

原因: JREが使用するWindowsプリンタ・ドライバに問題がある可能性があります。

解決方法: 使用中のプリンタのWindowsプリンタ・ドライバをアップグレードします。

2. 印刷が成功しているように見えるが、Windowsでジョブが印刷されない。

原因: 一部のジョブでは、プリンタへのスプールが正しく行われません。

解決方法: プリンタ・ドライバのプロパティで、「高度な印刷のオプション」を無効にします。

3. Windowsに印刷ダイアログ・ボックスが表示されるまでに長い時間がかかる。

原因: アプリケーションが、切断されているプリンタも含め、すべてのプリンタをJREにプローブさせている可能性があります。

解決方法: 切断されているか到達不可能なネットワーク・プリンタを探し、それらをプリンタのリストから削除します。

4. PrintJob.printDialog()がOracle SolarisおよびLinuxで「サービスが見つかりません」というエラーになる。

原因: 原因は次のいずれかです。

- lpcユーティリティが/usr/sbinディレクトリにありません。

- lpstatユーティリティが/usr/sbinディレクトリにありません。

解決方法: lpcやlpstatを、前述の標準の場所にインストールします。

13 Swing

この章では、Java SE Swing APIで発生する可能性のあるもっとも一般的な問題のいくつかをトラブルシューティングするための、具体的な手順に関する情報と指針を提供します。

この章には、次のセクションがあります。

- Swingの一般的なデバッグ・ヒント

- Swingの具体的なデバッグ・ヒント

Swingの一般的なデバッグ・ヒント

Java SE 6ではSwingのペイント・インフラストラクチャが大幅に変更されました。Java SE 6以降のリリースに固有のペイント・アーティファクトに気づいた場合は、新機能をオフにしてみることができます。これはプロパティswing.bufferPerWindowで行えます。

なにかしらのメニューがポップアップされた状態で実行されるSwingコードのデバッグを行う場合、デバッガをリモートで使用することをお薦めします。それ以外の場合、デバッグ・プロセスとアプリケーション実行とが互いにブロックし合い、システムでの後続の作業が行えなくなります。それが発生した場合に取れる唯一のアクションは、Oracle SolarisおよびLinuxのXサーバーを終了させることです。バグ・データベースに関する項を参照してください。

次は、いくつかの一般的なSwingの問題です。

- ペイント。

- レンダラ。

- 間違ったスレッドからのモデルの更新。

- ハング・アップ。

- 応答性。

- 再ペイントの問題。

- isOpaqueの使用。

- 起動: 小さなヒープ、不要なクラスのロードが原因である可能性があります。

次は、いくつかの検討事項です:

- ウィンドウごとのバッファ機能。

- ネイティブ・ルック・アンド・フィールの忠実度: Gnome対Windows

- Swingアプリケーションのフットプリント。

- JTable、JTree、およびJListはすべてレンダラを使用します。

- カスタム・レンダラの処理を極力減らすようにします。

- モデルの更新は、イベント・ディスパッチ・スレッドからしか行わないようにします。それ以外の場合、モデルの状態が表示に反映されなくなります。

次は、不適切なレンダラを識別します:

- 動作の遅いアプリケーション(特にスクロール時)。

- オプティマイザを使ってペイント呼出しを監視し、getTableCellTRendererComponentの呼出しを探します。

Swingの具体的なデバッグ・ヒント

Swingのいくつかの具体的なデバッグ・ヒント、起こり得る問題および回避方法に関する例。

次のトピックでは、Swingの問題およびトラブルシューティング手法を説明します。

- 不正なスレッド処理

- JComponentの子のオーバーラップ

- 表示の更新

- モデルの変更

- コンポーネントの追加または削除

- 不透明のオーバーライド

- グラフィックへの永続的な変更

- カスタム・ペイントとダブル・バッファリング

- 不透明なコンテンツ・ペイン

- 各セルのレンダラ呼出しのパフォーマンス

- リークの可能性

- 重量コンポーネントと軽量コンポーネントの混在

- Synthの使用

- イベント・ディスパッチ・スレッド上のアクティビティの追跡

- デフォルト・レイアウト・マネージャの指定

- リスナー・オブジェクトの不適切なコンポーネントへのディスパッチ

- コンテンツ・ペインへのコンポーネントの追加

- ドラッグ・アンド・ドロップのサポート

- コンポーネントの親は1つ

- JFileChooserとWindowsショートカットの問題

不正なスレッド処理

例外やペイント問題がランダムに発生するのは通常、Swingによる不正なスレッド使用の結果です。

Swingコンポーネントへのすべてのアクセスは、Javadocで特に明記されていないかぎり、イベント・ディスパッチ・スレッド上で行われる必要があります。これには、Swingコンポーネントに接続されるすべてのモデル(TableModelやListModelなど)も含まれます。

Swingの不正な使用をチェックする最良の方法は、次の例で示される計測RepaintManagerを使用することです。

public class CheckThreadViolationRepaintManager extends RepaintManager {
// it is recommended to pass the complete check
private boolean completeCheck = true;

public boolean isCompleteCheck() {
return completeCheck;
}

public void setCompleteCheck(boolean completeCheck) {
this.completeCheck = completeCheck;
}

public synchronized void addInvalidComponent(JComponent component) {
checkThreadViolations(component);
super.addInvalidComponent(component);
}

public void addDirtyRegion(JComponent component, int x, int y, int w, int
h) {
checkThreadViolations(component);
super.addDirtyRegion(component, x, y, w, h);
}

private void checkThreadViolations(JComponent c) {
if (!SwingUtilities.isEventDispatchThread() && (completeCheck ||
c.isShowing())) {
Exception exception = new Exception();
boolean repaint = false;
boolean fromSwing = false;
StackTraceElement[] stackTrace = exception.getStackTrace();
for (StackTraceElement st : stackTrace) {
if (repaint && st.getClassName().startsWith("javax.swing.")) {
fromSwing = true;
}
if ("repaint".equals(st.getMethodName())) {
repaint = true;
}
}
if (repaint && !fromSwing) {
//no problems here, since repaint() is thread safe
return;
}
exception.printStackTrace();
}
}
}

JComponentの子のオーバーラップ

JComponentの子同士のオーバーラップを許可した場合にも、ペイント問題が発生する可能性があります。

この場合、親がisOptimizedDrawingEnabledをオーバーライドしてfalseを返すようにする必要があります。isOptimizedDrawingEnabledをオーバーライドしないと、どのコンポーネントで再ペイントが呼び出されたかに応じて、コンポーネントが他のコンポーネントの上にランダムに表示される可能性があります。

表示の更新

表示を更新する必要があるときに再ペイントを正しく呼び出さなかった場合にも、ペイント問題が発生する可能性があります。

Swingコンポーネントの可視プロパティ(フォントなど)を変更すると、再ペイントまたは再有効化がトリガーされます。カスタム・コンポーネントを記述する場合には、表示やサイズ設定の情報が更新されるたびに再ペイント(とおそらく再有効化)を呼び出す必要があります。そうしないと、再ペイントが次回どこかでトリガーされるまで表示が更新されません。

これを診断する良い方法は、ウィンドウのサイズを変更することです。サイズ変更後にコンテンツが表示された場合、コンポーネントが再ペイントや再有効化を正しく呼び出さなかったことを意味しています。

モデルの変更

Swingコンポーネントの可視プロパティの変更時には再ペイントを呼び出します。モデルの変更時に再ペイントを呼び出す必要はありません。

モデルが正しい変更通知を送出すると、JComponentが必要に応じて再ペイントや再有効化を呼び出します。

ただし、モデルを変更したのに通知を送出しなければ、再ペイント・イベントが動作すらしない可能性があります。特に、これはJTreeでは動作しません。適切なモデル通知を送信するのが正しい方法です。その診断は通常、ウィンドウのサイズを変更し、表示が正しく更新されなかったことを確認することで行えます。

コンポーネントの追加または削除

コンポーネントを追加または削除した場合は、再ペイントを手動で呼び出すか、SwingおよびAWTを再有効化する必要があります。

不透明のオーバーライド

ペイント問題の別の潜在的な領域は、コンポーネントが不透明をオーバーライドしない場合です。

さらに、実装を呼び出さない場合は、不透明プロパティを尊重する必要があります(このコンポーネントが不透明な場合は、不透明でない色でバックグラウンドを完全に塗りつぶす必要があります)。不透明プロパティを尊重しない場合は、視覚的なアーティファクトが見える場合があります。

これをチェックする唯一の方法は、コンポーネントが再ペイントを呼び出す際に一貫性のある視覚的なアーティファクトを探すことです。

グラフィックへの永続的な変更

paint、paintComponent、またはpaintChildrenに渡されるGraphicsに対する永続的な変更は、一切行わないでください。

注意:

サブクラスのグラフィックをオーバーライドする場合は、Graphicsで渡されるpaint、paintComponentまたはpaintChildrenを永続的に変更しないでください。たとえば、クリップRectangleを変更したり、変換を変更したりするべきではありません。このような操作が必要な場合は、渡されたGraphicsから新しいGraphicsを作成し、それを操作するほうが容易でしょう。

この制限を無視すると、クリッピングなどの奇妙な視覚的アーティファクトが発生します。

カスタム・ペイントとダブル・バッファリング

paintをオーバーライドし、そのオーバーライド内でカスタム・ペイントを行うことも可能ですが、代わりにpaintComponentをオーバーライドすべきです。

JComponent.paintメソッドは、ペイントがダブル・バッファに対して発生することを保証します。paintを直接オーバーライドすると、ダブル・バッファリングが失われる可能性があります。

不透明なコンテンツ・ペイン

Swingのペイント・アーキテクチャでは不透明なコンテンツ・ペインが必要です。

Swingのペイント・アーキテクチャでは、不透明なJComponentが包含関係の階層の中でほかのすべてのコンポーネントの上に存在する必要があります。通常、これはコンテンツ・ペインを使用して提供されます。コンテンツ・ペインを置き換える場合は、setOpaque(true)を使用してコンテンツ・ペインを不透明にすることをお薦めします。また、コンテンツ・ペインによってpaintComponentがオーバーライドされる場合は、バックグラウンドをpaintComponentの不透明な色で完全に塗りつぶす必要があります。

各セルのレンダラ呼出しのパフォーマンス

レンダラはセルごとにペイントされるので、レンダラの処理は極力少なくしてください。

レンダラ内での速度低下はすべて、すべてのセルにわたって拡大されます。たとえば、50x20の可視セルで構成される表の可視領域を再ペイントすると、レンダラが1000回呼び出されます。

可能性のあるリーク

モデルのライフ・サイクルが、モデルを使用するコンポーネントを含むウィンドウのライフ・サイクルよりも長い場合、Swingコンポーネントのモデルを明示的にnullに設定する必要があります。

モデルをnullに設定しなかった場合、モデルがComponentへの参照を保持しているため、ウィンドウ内のコンポーネントはすべて、ガベージ・コレクションの対象外となります。次の例を見てみましょう。

TableModel myModel = ...;
JFrame frame = new JFrame();
frame.setContentPane(new JScrollPane(new JTable(myModel)));
frame.dispose();

アプリケーションがまだmyModelへの参照を保持している場合、frameとそのすべての子には、引き続きmyModelにインストールされたリスナーJTable経由で到達できます。解決方法は、table.setModel(new DefaultTableModel())を呼び出すことです。

重量コンポーネントと軽量コンポーネントの混在

重量コンポーネントと軽量コンポーネントが混在しても、重量コンポーネントが既存のSwingコンポーネントと重複しなければ、特定のシナリオでは動作する場合があります。

たとえば、重量コンポーネントは内部フレームでは機能しません。ユーザーが内部フレームの周辺をドラッグすると、他の内部フレームと重複してしまうためです。重量コンポーネントを使用する場合は、次のメソッドを呼び出します。

- JPopupMenu.setDefaultLightWeightPopupEnabled(false)

- ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false)

Synthの使用

Synthは空のキャンバスです。

Synth,を使用するには、ルック・アンド・フィールを構成する完全なXMLファイルを用意するか、あるいはSynthLookAndFeelを拡張して独自のSynthStyleFactoryを提供します。

イベント・ディスパッチ・スレッド上のアクティビティの追跡

Swingアプリケーションがイベント・ディスパッチ・スレッド上で実行を試みる処理が多すぎると、アプリケーションの動作が遅くなり、無反応になります。

この状況を検出する方法の1つは、処理時間の長すぎるイベントが発生した際にロギング情報を出力できる、新しいEventQueueをプッシュすることです。このアプローチは、フォーカス・イベントやモーダリティに問題があるので完璧とは言えませんが、アドホックなテストには有効です。

デフォルト・レイアウト・マネージャの指定

1つのSwingコンポーネント上に様々なデフォルト・レイアウト・マネージャ・クラスがあると、問題が生じる可能性があります。

たとえば、JPanelクラスのデフォルトはFlowLayoutですが、JFrameクラスのデフォルトはBorderLayoutです。この状況は、1つのLayoutManagerを指定することで簡単に解決されます。

リスナー・オブジェクトの不適切なコンポーネントへのディスパッチ

MouseListenerオブジェクトは、MouseListenerオブジェクトを持つ(またはMouseEventオブジェクトを有効化した)もっとも深いコンポーネントにディスパッチされます。

このため、ユーザーがMouseListenerをコンポーネントに接続しても、そのコンポーネントにMouseListenerオブジェクトを持つ子孫が含まれていれば、ユーザーのMouseListenerオブジェクトが呼び出されることは決してありません。

このことは、編集可能なJComboBoxのような複合コンポーネントで容易に再現されます。JComboBoxにはMouseListenerを持つ子コンポーネントが含まれているため、編集可能なJComboBoxに接続されたMouseListenerが通知を受け取ることは決してありません。

ユーザーのMouseListenerが突然イベントを取得しなくなった場合、それは、アプリケーション内でなんらかの変化が生じ、それによって子孫コンポーネントの1つがMouseListenerを持つようになった結果である可能性があります。これをチェックする良い方法は、子孫に対して繰返し処理を実行し、各子孫がマウス・リスナーを持っているかどうかを確認することです。

KeyListenerクラスでも似たようなシナリオが発生します。KeyListenerオブジェクトは、フォーカスされたコンポーネントにしかディスパッチされません。

JComboBoxのケースはこの状況のもう1つの例です。編集可能なJComboBoxの場合、JComboBoxではなくエディタがフォーカスを得ます。結果として、編集可能なJComboBoxに接続されたKeyListenerが通知を受け取ることは決してありません。

コンテンツ・ペインへのコンポーネントの追加

JFrame、JWindow、JDialogまたはJAppletコンポーネントをコンテンツ・ペインに追加する必要があります。

J2SE 1.5より前は、JFrame、JWindow、JDialog、またはJAppletにコンポーネントを追加することはできませんでした。代わりに、コンテンツ・ペインにコンポーネントを追加する必要がありました。J2SE 1.5以降でも、トップ・レベルのSwingコンポーネントに追加されるコンポーネントはコンテンツ・ペインに追加される必要があることに変わりはありませんが、これらのクラスのaddメソッド(およびいくつかのほかのメソッド)はコンテンツ・ペインにリダイレクトされます。つまり、frame.getContentPane().add(component)はframe.add(component)と同じです。

次のメソッドはコンテンツ・ペインに自動的にリダイレクトされます。add (およびそのバリアント)、remove (およびそのバリアント)、setLayout。

これは非常に便利ですが、混乱を招く恐れもあります。特に、getChildren、getLayout、およびその他の各種メソッドは、コンテンツ・ペインにリダイレクトされません。

この変更は、GroupLayoutやBoxLayoutなどの1つのコンポーネントのみを処理するLayoutManagerに影響を及ぼします。たとえば、新しいGroupLayout(frame)は機能しません。かわりに、GroupLayout(frame.getContentPane())を使用する必要があります。

ドラッグ・アンド・ドロップのサポート

Swingを使用する際は、Swingのドラッグ・アンド・ドロップ・サポート(TransferHandlerによって提供されるもの)を使用するようにしてください。

コンポーネントの親は1つ

コンポーネントは一度に1つの親の中にしか存在できません。

メニュー間でメニュー項目を共有すると、問題が発生します。たとえば、JMenuItemはコンポーネントなので、一度に1つのメニューの中にしか存在できません。

JFileChooserとWindowsショートカットの問題

JFileChooserクラスは、Windows OSのショートカット(.lnkファイル)をサポートしていません。

JFileChooserでは標準のWindowsファイル・チューザと違って、ファイル・システムの参照時にWindowsショートカットを辿ることができません。ファイルへの正しいパスが表示されないからです。

問題を再現するための手順:

1. デスクトップで、たとえばMyFile.txtという名前のテキスト・ファイルを作成します。テキスト・ファイルを開き、なんらかのテキスト、たとえばThis is the contents of MyFile.txtを入力します。

2. 新しいテキスト・ファイルへのショートカットを、次のようにして作成します。マウスの右ボタンでファイルをデスクトップの別の場所にドラッグし、「ショートカットをここに作成」を選択します。

3. JfileChooserテスト・アプリケーションを実行し、デスクトップを参照して「MyFile.txtへのショートカット」を選択し、「開く」をクリックします。

4. 結果ファイルはデスクトップへのパス\MyFile.txtへのショートカット.lnkになっていますが、これはデスクトップへのパス\MyFile.txtである必要があります。

5. さらに、テキスト領域の結果ファイルの内容として、MyFile.txtへのショートカット.lnkファイルの内容が表示されますが、この内容は、手順1で入力したThis is the contents of MyFile.txtになるはずです。

14 国際化

国際化サポートの領域で発生する可能性のある問題をトラブルシューティングするための情報と指針。

詳細については、Java国際化に関するサイトにアクセスしてください。

この章では、国際化およびローカリゼーションのトラブルシューティング手法について説明します。

- 国際化およびローカリゼーションのトラブルシューティング

国際化およびローカリゼーションのトラブルシューティング

国際化とローカリゼーションの違いのトラブルシューティング。

トラブルシューティングを始める前に、国際化とローカリゼーションの違いを必ず理解しておいてください。

- 国際化とは、様々な言語や地域への適応(ローカライズ)を、容易に低コストでソフトウェアを変更せずに行えるようにソフトウェアを設計するプロセスのことです。このプロセスには一般に、プログラム内で言語や文化に依存している部分を分離する作業が含まれます。たとえば、エラー・メッセージのテキストはプログラム・ソース・コードから分離された状態に保たれますが、それは、ローカリゼーション時にメッセージを翻訳する必要があるからです。

- ローカリゼーションとは、特定ロケールでの使用にプログラムを適応させるプロセスのことです。ロケールとは、同じ言語や慣習を共有する地理的または政治的な地域のことです。ローカリゼーションには、ユーザー・インタフェース・ラベル、エラー・メッセージ、オンライン・ヘルプなどのテキストの翻訳が含まれます。また、データ項目(通貨値、時間、日付、数値など)の文化固有の書式設定も含まれます。

Java SEプラットフォームのユーザー・インタフェース・ライブラリを使えば、インタラクティブなリッチ・アプリケーションを開発できます。国際化の側面にはテキスト入力、テキスト表示、ユーザー・インタフェース・レイアウトが含まれます。次の説明は、国際化と、AWT、Java 2D、およびSwing APIで提供される機能との関係を示したものです。

- テキスト入力とは、キーボードをタイピングするか、インプット・メソッド、手書き認識、音声入力などのフロントエンド・ソフトウェアを使用して、ドキュメントに新しいテキストを入力するプロセスのことです。

- テキスト表示はマルチステップのプロセスであり、フォントの選択、段落や行へのテキストの配置、文字または文字シーケンスのグリフの選択、それらのグリフのレンダリング、を含みます。一部の書記法では、双方向テキスト・レイアウトや複雑な文字からグリフへのマッピングが必要となります。テキスト表示は、軽量ユーザー・インタフェース・コンポーネントではJava 2Dグラフィックス・システムとSwingツールキットによって、ピア・ユーザー・インタフェース・コンポーネントではAWTによって、それぞれ処理されます。

- ユーザー・インタフェース・レイアウトは、ローカリゼーションによって発生するテキストの拡大や縮小に対応するほか、ユーザーの書記法の方向に一致する必要があります。

15 Javaサウンド

この章では、Javaサウンド・テクノロジで発生する可能性のあるいくつかの問題を説明し、その原因と回避方法を提示します。

次のトピックでは、Javaサウンドの問題のトラブルシューティング・シナリオについて説明します。

- Javaサウンドの問題のトラブルシューティング

Javaサウンドの問題のトラブルシューティング

システム・サウンド構成、オーディオ・ファイル形式、オーディオ形式、およびオーバーランとアンダーランの条件など、Javaサウンドの問題をトラブルシューティングします。

- システム・サウンド構成:
使用中のオーディオ・システムが正しく構成されていることを確認します(Windows: サウンド・カード・ドライバ/DirectSound、Linux: ALSA、Oracle Solaris: オーディオ・ミキサー)。さらに、スピーカが接続されており、サウンド・カードのボリュームとミュートの状態が適切な値に調整されていることを確認します。サウンドの構成をテストするには、任意のネイティブ・サウンド・アプリケーションを実行し、そこで何らかのサウンドを再生します。

Oracle SolarisおよびLinuxオペレーティング・システムではサウンドを再生できない可能性がありますが、それは、アプリケーション(またはesdやartsdなどのサウンド・デーモン)がオーディオ・デバイスを排他的に開くために、デバイスへのJava Soundのアクセスが拒否されるためです。

- オーディオ・ファイル形式:
Java SoundはAU、AIF、WAVなど、一連のオーディオ・ファイル形式をサポートしています。ほとんどのファイル形式は単なるコンテナなので、さまざまな圧縮オーディオ形式のオーディオ・データを格納できます。Java Soundのファイル・リーダーはいくつかの形式(非圧縮PCM、a-law、mu-law)をサポートしていますが、ADPCMやMP3などはサポートしていません。

また、Java Soundはサービス・プロバイダ・インタフェース(SPI)経由でファイル・リーダー/ライター用のプラグインもサポートしています。Sun、サード・パーティ、またはユーザー独自のプラグインを使ってさまざまなオーディオ・ファイルを読み取れます。いずれにしても、必要なプラグインをアプリケーションと一緒に配布したり、クライアントのJava環境にプラグインがインストールされることを要求したりするなどして、プラグインの存在を管理する必要があります。

- オーディオ形式:
Java Soundは様々なオーディオ形式をサポートしていますが、それらを利用できるかどうかはオペレーティング・システムごとに異なります。録音または再生用になんらかのオーディオ形式を使用するには、使用しているシステム(サウンド・カード・ドライバ)でその形式がサポートされている必要があります。サポートされている形式をできるだけ使用してください(PCM、8または16ビット、8000、11025、22050、44100 Hz)。これらの形式は、存在しているサウンド・カードのほとんどでサポートされています。ほとんどのサウンド・カードはPCM形式しかサポートしていないため、そのドライバがmu-lawをサポートしている場合でも、ソフトウェアに多少の変更を加える必要があります。mu-lawデータを録音または再生する必要がある場合は、形式コンバータによってそれをPCM形式に変換するのが望ましい方法です。

形式変換の詳細については、AudioSystem.getAudioInputStreamのドキュメントを参照してください。

- オーバーランとアンダーランの条件:
録音データはDataLineバッファ内に格納されます。長い間ラインからの読出しが行われなかった場合、オーバーラン状態が発生し、古いデータが新しいデータに置き換えられます。このため、録音されたオーディオ・データにアーティファクトが発生します。

再生時も似た状況が発生します。バッファ内のすべてのデータが再生されたとき、ラインに新しいデータが書き込まれていない場合、アンダーラン状態が発生し、ラインにオーディオ・データの新しい部分が書き込まれるまで無音になります。

推奨の録音方法は、ほかのタスク(UI処理など)の潜在的な影響を受けないように、独立したスレッドでデータを読み取ることです。再生にSourceDataLineを使用する場合は、ラインにデータを書き込むためのスレッドを個別に用意することをお薦めします。再生にClipを使用する場合、Clipの実装自体によってこのタイプのスレッドが作成されます。

16 アプレットとJava Web Startアプリケーション

JavaアプレットとJava Web Startアプリケーションのデプロイに関連のある問題および解決方法についての説明。

この章の構成は、次のとおりです。

- 構成の問題

- アプレットのトラブルシューティング

- セキュリティ・ダイアログ・ボックスの回避

- 開発のヒント

構成の問題

アプレットおよびJava Web Startアプリケーションの構成の問題を解決するためのトラブルシューティングの手法。

次の項では、様々な構成パラメータおよび設定に関する多くの問題について説明し、アプレットおよびJava Web Startアプリケーションの構成のトラブルシューティング手法を提案します。

- 検証

- 一般的な構成の問題

- Javaランタイムの管理

- JREへのパラメータの受渡し

- Javaデプロイメント・ホーム

- デプロイメントのトレース

- デプロイメント・キャッシュ

- ネットワーク構成

検証

アプリケーションが動作しない場合の検証手法。

アプリケーションが実行されない場合は、次のチェックを実行してください。

1. Java Plug-inが動作していることを確認します。

- 「Javaのバージョンの確認」に移動します。

- 「Javaのバージョンの確認」をクリックします。

- 画面上で予想どおりのJavaテクノロジ・バージョンが報告された場合、プラグインは有効で、検出されています。

2. ブラウザがJavaプラグインを認識していることをチェックします。

- Chrome: アドレス・バーで「about: plugins」と入力します。

- Firefox: 「ツール」メニューで、「アドオン」を選択して「プラグイン」をクリックします。

- Internet Explorer: 「ツール」メニューで、「アドオンの管理」を選択します。

3. Javaランタイム環境がインストールされていることを確認します。

Windowsの場合、コントロール・パネルでインストール済みプログラムの一覧をチェックします。

一般的な構成の問題

一般的な構成の問題を解決するためのトラブルシューティング手法。

次は、一般的な構成の問題のトラブルシューティング手法です。

- Javaランタイムのインストール:

Java Developer Kitをインストールするだけでは十分ではありません。アプレットまたはJava Startアプリケーションを実行するにはJREが必要です。

注意:
JREには64ビット・ブラウザが必要です。

- 一致する最新のプラグイン/Web Startの使用:

ブラウザに登録できるプラグインは1つだけであり、JREは常に、システム上の最新のJREに含まれるプラグインをアクティブとして登録します。古いプラグインが確実に使用されるようにするための唯一の方法は、新しいJREをアンインストールすることです。

- Javaランタイム構成に変更を加えた場合のブラウザの再起動:

Javaが有効になっていないか、Javaが複数の場所で無効化されている可能性があります。Javaコントロール・パネルと、ブラウザのプラグイン/アドオン・リストをチェックしてください。

- 新しい世代のプラグインが有効であることの確認:

レガシー・モードでの実行が必要でないかぎり、Javaコントロール・パネルで次世代のプラグインが有効になっていることを確認してください。

- JavaScriptの有効化:

JavaScriptが無効になっていると、Javaアプレットの起動の試みが非常に早い段階で失敗する可能性があります。

- 前回使用時間トラッキングの無効化:
JREは前回使用された時刻をトラッキングします。アプレットまたはJava Web Startアプリケーションに対して前回使用された時間、コマンド行や他の方法で起動された時間がファイルに記録されます。
デフォルトでは、前回使用時間トラッキングが有効になります。前回使用時間トラッキングを無効にするには、Java Usage Trackerプロパティ・ファイルのJava Usage Trackerプロパティcom.oracle.usagetracker.track.last.usageをfalseに設定します。詳細は、Java Platform, Standard Edition Usage TrackerガイドのJava Usage Trackerプロパティに関する項を参照してください。

前回使用時間トラッキングを有効にすると、オペレーティング・システムに応じて、次のディレクトリのいずれかにファイルが作成されます。

- Windows: %ProgramData%\Oracle\Java\.oracle_jre-usage\

- 他のすべのオペレーティング・システム: ${user.home}/.oracle_jre_usage/

注意:

商用機能であるJava Usage Tracker、および商用機能ではない前回使用時間トラッキングは別々に有効になるため、1つを無効にしても、もう1つは無効になりません。

- Javaコントロール・パネルでのJavaの設定への変更の失敗:

UACがオンになっているWindows 7またはWindows Vistaシステムでは、Javaコントロール・パネルがグローバル・レジストリ設定の更新に失敗する可能性があります。これを回避するには、これらの設定を変更する必要がある場合に、必ずJavaコントロール・パネルを管理者として起動してください。

Javaランタイムの管理

Javaコントロール・パネル・ツールを使えば、インストール済みの一連のJava Runtime Environmentとその動作を管理できます。

Javaコントロール・パネルは、JREインストール・フォルダのbinディレクトリから起動できます。Windowsオペレーティング・システムの場合、「コントロール パネル」>「Java」からもアクセスできます。

次を行う必要がある場合にJavaコントロール・パネルを使用します。

- インストール済のJREおよびアクティブなJREのバージョンを確認する

- 特定バージョンのJREの使用を一時的に有効または無効にする

- アプレットまたはWeb Startアプリケーションの起動時にJVMに渡される「グローバル」パラメータを設定する

- 特定ブラウザでのJava Pluginの使用を有効または無効にする

- プラグインまたはWeb Startアプリケーションの動作を調整するため、図16-1に示すように、構成パラメータを指定する(一時ファイルのキャッシュの場所、トレースの有効化など)

図16-1 「Java Runtime Environment設定」ウィンドウ

JREへのパラメータの受渡し

トラブルシューティング、デバッグ、プロファイリングなどの開発アクティビティでは、JVMの起動時に特別なパラメータ・セットを指定する必要がある場合があります。これを実現する方法の1つは、Javaコントロール・パネルを使用することです。

Javaコントロール・パネルを開き、「Java」タブの「表示」をクリックします。変更するJREの「ランタイム・パラメータ」セルを選択し、このセルにパラメータを入力します。

注意:

これらの変更はグローバルであり、このバージョンのJREを使用して実行されるJava Web Startアプリケーションまたはアプレットでは、appletタグやJNLPファイルでの指定に加えて、これらのパラメータが設定されます。

Java Web Startやアプレットで使用される特定のJVMにパラメータを渡すには、次のいずれかの手法を使用します。

- javawsまたはブラウザのプロセスを起動する前に環境変数を設定します。

- JAVAWS_VM_ARGS (Java Web Startアプリケーション用)。次に例を示します。

JAVAWS_VM_ARGS = -Dsome.property=true

- _JPI_VM_OPTIONS (アプレット用)。次に例を示します。

_JPI_VM_OPTIONS = -Dsome.property=true

注意:

環境変数の設定が完了したら、ブラウザを再起動する必要があります。この環境変数をコマンド・シェルで設定した場合は、環境変数の値がブラウザに継承されるように、同じコマンド・シェルを使ってブラウザを起動する必要があります。

- javawsコマンドで-Jオプションを使用します。次に、例を示します。

javaws -J-Dsome.property=true http://example.com/my.jnlp

Javaデプロイメント・ホーム

これは、主な構成ファイルが保持されている場所です。この場所は、オペレーティング・システムに固有です。

- Windows XP: %HOME%\Application Data\Sun\Java\Deployment

- Windows 7/Vista: %APPDATA%\..\LocalLow\Sun\Java\Deployment

- Oracle Solaris/Linux: %HOME%/.java/deployment

デプロイメントのトレース

Java Plug-inとJava Web Startは両方とも、トレース情報をトレース・ファイルに出力できます。これにはJRE自体からのログ情報と、アプリケーションからSystem.outまたはSystem.errに出力されるすべての情報が含まれます。

トレース情報にアクセスするには、次のようにします。

1. Javaコントロール・パネル(jre_home_dir/bin/ControlPanel)を開きます。

2. 「詳細設定」タブを選択します。

3. 「デバッグ」カテゴリで、「トレースを有効にする」チェック・ボックスを選択します。

4. (オプション)「Javaコンソール」カテゴリで「コンソールを表示する」オプションを選択すると、トレース情報がコンソール・ウィンドウに表示されます。それでもファイルには完全なトレース・ファイルが保存されます。

トレース・ファイルは、Javaデプロイメント・ホーム・フォルダの logディレクトリに保存されます。Javaデプロイメント・ホームを参照してください。ファイル名には、実行している対象によってjavawsまたはpluginの接頭辞がつきます。プロセスにつき1つのトレース・ファイルが作成されますが、1つのアプリケーションは複数のプロセスを使用して起動できます。

トレース・ファイルの詳細レベルを最大にするには、deployment.propertiesファイル(Java配備ホーム・ディレクトリにあります)を編集して次の行を追加します。

deployment.trace.level=all

デフォルトでは、最大5つのトレース・ファイルが作成されます。もっとも古いトレース・ファイルが自動的に削除されます。トレース・ファイルの最大数の制限を変更するには、deployment.propertiesファイルに次の行を追加します。

deployment.max.output.files=max_number_of_trace_files

Javaコンソールを使用すると、実行中にトレース・ログを表示できます。デフォルトでは、Javaコンソールは非表示です。これをJavaコントロール・パネルで有効にしてください。

デプロイメント・キャッシュ

アプリケーションのJARとリソースは、次回必要になった際にロードしないで済むように、ディスク上にキャッシュされます。

キャッシュのデフォルトの場所はオペレーティング・システムに依存しますが、Javaコントロール・パネルでオーバーライドできます。

キャッシュの設定や制御は、Javaコントロール・パネルの「一般」タブの「インターネット一時ファイル」セクションで使用できます。キャッシュの場所やサイズを変更するには、「設定」をクリックします。キャッシュ内のファイルを表示するには、「表示」をクリックします。

キャッシュをクリーン・アップするには、javaws -uninstallを実行するか、Javaコントロール・パネルの「一般」タブを開き、「表示」をクリックして、ファイルを手動で削除します。また、Javaコントロール・パネルを使って個々のアプリケーションや拡張をアンインストールすることもできます。

ネットワーク構成

一般に、Java Web Startアプリケーションはデフォルトでシステムのネットワーク構成を使用し、アプレットはブラウザのネットワーク設定を使用します。Javaコントロール・パネルを使用すれば、ネットワーク・プロキシを明示的に設定できます。

特に、Javaテクノロジのネットワーク層は、使用する必要があるネットワーク・スタックを自動的に検出します。ただし、自動検出が動作しないときがあり、アプリケーションやアプレットをダウンロードするソケットを開こうとした際にアクセス権拒否(Permission Denied)例外が発生することがあります(他のツールでは同じプロキシ設定を使用して同じURLにアクセスできる場合でも)。この問題は、一部のWindows 7システムでVPNソケットを使用した場合に発生しました。これを解決するには、JVMに明示的にパラメータを渡します。

-Djava.net.preferIPv4Stack=true

「JREへのパラメータの受渡し」を参照してください。

アプレットのトラブルシューティング

タブをサポートする最新のブラウザでは、各タブがそれぞれ独立したブラウザ・プロセスである可能性があります。Javaアプレットがブラウザ・ページに埋め込まれていて、次世代プラグインが使用されている場合、通常、そのブラウザ・タブに関連付けられたプロセスが、そのプロセスの内部にJVMを作成します(ブラウザVM)。このブラウザVMは、アプレットの実行やアプレットのライフ・サイクルの管理を行う、別のJVMプロセス(クライアントVM)を作成します。クライアントVMは1つのJavaプロセスです(Windowsではjava.exe、Oracle Solaris/Linuxプラットフォームではjava)。

次に、アプレットの問題およびトラブルシューティング手法を示します。

- アプレットを起動するためのプラグインのチート・シート

- ブラウザまたはJavaプロセスのクラッシュ

- 応答のないWebページ

アプレットを起動するためのプラグインのチート・シート

アプレットが起動しない場合は、前述したように、必ずトレースとJavaコンソールを有効にしてください。次に、次のヒントを使用してアプレットが動作しない理由を調べます。

トレース・ファイルが生成されたり、Javaコンソールが表示されたりしますか。

- いいえ、トレース・ファイルを得られません
Javaテクノロジが検出されているかどうかを確認します。「検証」を参照してください。

- 検出されている場合
「ブラウザまたはJavaプロセスのクラッシュ」のJVMブラウザの問題を参照してください。

- 検出されていない場合
これはおそらく構成の問題です。「一般的な構成の問題」を参照し、それでもわからない場合は、「ブラウザまたはJavaプロセスのクラッシュ」のJVMブラウザの問題を参照してください。

- はい、トレース・ファイルがあります
これは(次世代プラグインを無効にしていないかぎり)、おそらく構成の問題ではありません。この問題はおそらくこのアプレットに固有のものです。確認のため、ほかのアプレットもいくつか起動してみてください。「ブラウザまたはJavaプロセスのクラッシュ」のJVMクライアントの問題を参照してください。

ブラウザまたはJavaプロセスのクラッシュ

クラッシュは、プラットフォームの問題かアプリケーションの問題によって発生する可能性があります。

通常、JVMでクラッシュが発生すると、現在の作業ディレクトリにhs_err_*logファイルが作成されます。Windowsでは、多くの場合デスクトップにあります。これは、スタンドアロン・アプリケーションと同じクラッシュ・レポート・ファイルです。「致命的エラー・ログ」を参照してください。

配備キャッシュ・ディレクトリからロードされたネイティブ・ライブラリが見つかった場合に、特にそれらのライブラリのコードがクラッシュ・スタックに含まれている場合は、アプリケーションのバグである可能性が非常に高くなります。

それ以外の場合は、JREのバグであり、バグ・データベースに報告する必要があります。

プラットフォームまたはアプリケーションの問題によるクラッシュの場合に考慮すべき2つのシナリオを次に示します。

- JVMブラウザの問題: ブラウザ・プロセスで実行されているJVMの詳細を取得してください。ブラウザを起動する前に、次の2つの環境変数を設定します:

JPI_PLUGIN2_DEBUG=1
JPI_PLUGIN2_VERBOSE=1

Windowsの場合、ブラウザ・プロセスに関連付けられたコマンド・ウィンドウが存在するはずです。ブラウザVMのデバッグ出力はすべて、コマンド・ウィンドウに表示されます。そこに例外が表示されていないかチェックします。Javaスレッド・ダンプを取得するには、コマンド・ウィンドウで[Ctrl]+[Break]キー・シーケンスを使用します。

Oracle SolarisまたはLinuxプラットフォームの場合、これらの変数を設定した後、同じセッションからブラウザを起動します。ブラウザVMのデバッグ出力はすべて、端末ウィンドウに表示されます。Javaスレッド・ダンプを取得するには、別の端末からkill -3 pidまたはkill -SIGQUIT pidを使用します(pidはブラウザ・プロセスのプロセスID)。

クライアントVMとブラウザVM間ではハートビート・メッセージが送信されます。このハートビート・メッセージをオフにするには、JPI_PLUGIN2_NO_HEARTBEAT環境変数を1に設定します。これは、問題がハートビート関連かどうかを区別するのに役立ちます。

ログが開かれず、ブラウザ・プロセス内で環境変数が設定されている場合、JREが正しくインストールされていないか、Javaが無効になっている可能性があります。どうしても解決できない場合は、構成エラーがないかチェックし、JREの再インストールを試みてください。

- JVMクライアントの問題: 最新のトレース・ファイルをチェックしてヒントを得ます。
注意: 同じクライアントJVMを複数のアプレットで共有することができます。共有JVMで、使用可能なリソース(ヒープ・サイズなど)が不足したために、間欠的な失敗が発生する場合があります。この場合は、ページを再ロードすると問題が解決することがあります。

アプリケーションがメモリー不足(Out of memory)エラーで失敗する場合は、ヒープ・サイズを増やす必要があります。これは、アプリケーション配備記述子(JNLPファイル)で行うことも、Javaコントロール・パネルで、使用するJREのランタイム・パラメータを使用して行うこともできます。

アプリケーションが署名付きであり、ユーザーがセキュリティ・ダイアログ・ボックスで拒否した場合、アプリケーションが失敗する可能性があります。ユーザーが行なった決定は、JVMが再起動するまで記憶されます。セキュリティ・ダイアログ・ボックスを再び表示させるには、ユーザーがブラウザを再起動する必要がある場合があります。

応答のないWebページ

応答のないWebページが生じる可能性があるシナリオを次に示します。

- アプレットの起動または実行時にアプレットがフリーズする:
アプレットの起動時または実行時にアプレットがフリーズする原因は、Liveconnectの呼出しである可能性があります。

起動時にJavaScriptからJavaアプレットにアクセスしようとすると、アプレットの初期化が完了するまでJavaScriptエンジンがブロックされる可能性があります。アプレットの準備が整うまでJavaScriptアクセスを延期し、enableStatusEventsパラメータを使用してアプレット・ステータス・チェックへの非ブロック・アクセスのロックを解除することをお薦めします。

実行時にLiveConnectを使用するには、JavaScriptの呼出しをすばやく復帰させることで、シングル・スレッドのJavaScriptエンジンがブロックされないようにすることをお薦めします。

- アプレットまたはブラウザがハングする:
この場合の最良の情報源は、クライアントJVMとブラウザJVMの両方のスタック状態です。

jstackを使用してブラウザJVM (jstack browser-pidを実行)とクライアントJVMのJVMスタック・ステータスを収集します。注意: これらのいずれかのVMのコンテキストでデッドロックが発生した場合、jstackはデッドロックをハイライトできますが、デッドロックに両方のプロセスが含まれる場合にはこれを行うことができません。この場合、スレッド・スタックを手動で調べる必要があります。「jstackユーティリティ」を参照してください。

「プロセスのハングおよびループのトラブルシューティング」を参照してください。

セキュリティ・ダイアログ・ボックスの回避

Java Runtimeは、発生する可能性のあるセキュリティに関連する問題について、ユーザーに自動的に警告します。使用するアプリケーションが安全であると確信できる場合には、セキュリティ・ダイアログ・ボックスをバイパスしてユーザー体験を簡素化できます。

セキュリティ・ダイアログ・ボックスを回避する2つのシナリオを次に示します。

- 署名付きのアプリケーション

- 混合コードの問題

署名付きのアプリケーション

JavaアプレットまたはWeb Startアプリケーションが署名付きの場合、証明書セキュリティ警告ダイアログ・ボックスが表示されるので、「実行」をクリックしてアプリケーション・コードにすべての権限を与える必要があります。

このダイアログ・ボックスが表示されないようにするには、次のいずれかを実行します。

- アプリケーションの署名に使用された証明書を受け入れ、「この発行者からのコンテンツを常に信頼」チェックボックスを選択します。これで、次回からこのアプリケーションに権限が自動的に付与されます(証明書が期限切れになるか、信頼できるキーストアから削除されるまで)。

- 証明書は、JREの信頼できる証明書ストアに手動でインポートできます。Javaコントロール・パネルを使用して証明書をインポートするには、「セキュリティ」タブで、「証明書」→「信頼できる証明書」をクリックします。証明書ストアへの証明書のインポートをコマンド行から行うには、keytoolユーティリティ(JREのbinフォルダ内)を使用します。

- ${user.home}/.java.policyにあるJavaポリシー・ファイルでAllPermissionsを付与するか、$(JRE_HOME)/lib/security/java.securityファイル内で、AllPermissionsを含む任意のJavaポリシー・ファイルをポイントします。権限は、すべてのアプリケーションに付与することも、特定のURLに制限することもできます。.java.policyの詳細は、「デフォルトのPolicyの実装とポリシー・ファイルの構文」を参照してください。

注意:

自動的な権限付与が望ましくない場合は、Javaコントロール・パネルを使って信頼できる証明書キーストアから証明書を削除してください。これを行うと、セキュリティ・ダイアログ・ボックスが表示されます。

混合コードの問題

署名付きと署名なしのコンポーネントを含む署名付きのJava Web Startアプリケーションとアプレットは、混合コードがアプリケーション・ベンダーによって意図されたものでないかぎり、安全でない可能性があります。最新バージョンのJavaランタイムでは、署名付きコンポーネントと署名なしコンポーネントの両方がプログラムに含まれていて不審な使用が検出された場合には、混合コード警告ダイアログが表示されます。

通常、このダイアログ・ボックスを回避するには、アプリケーションの実装に変更を加えるか、アプリケーションを再パッケージングする必要があります。信頼できるコードと信頼できないコードの混在をソフトウェアで一切チェックしないようにすることも可能ですが、これによってユーザーは安全でない可能性のあるコードを警告も追加の保護もなく実行できるようになるためお薦めしません。

開発のヒント

Java Web Startアプリケーションとアプレットでは、直接起動のかわりにアタッチ・メカニズムを使用する必要あることを除き、スタンドアロン・アプリケーションのデバッグおよびプロファイリングで利用できる手法のほとんどを使用できます。

注意:
プラグインとJava Web Startは、実際にアプリケーションまたはアプレットを実行するJVMを実行する追加のjavaまたはjavawプロセスを生成します。アプレットに関する情報を収集できるように、これらのプロセスにアタッチする必要があります。たとえば、アプレットのメモリー・ダンプを取得するには、PluginMainクラスを実行しているjavaプロセスのプロセスIDを調べる必要があります。たとえば、JDKからjpsユーティリティを使用した後、jmapユーティリティを使用してメモリー・ダンプを取得します。

アプレットのデバッグのための開発のヒントを次に示します。

- アプレットおよびWeb Startアプリケーションのデバッグ:

スタンドアロンJavaアプリケーションの場合と同様に、アプレットまたはWeb Startアプリケーションのデバッグには、任意のJPDAベースのデバッガを使用できます。たとえば、「jdbユーティリティ」 またはNetBeansデバッガを参照してください。

アプリケーションを実行するJVMでJDWPエージェントを有効にし、ポート番号を指定する必要があります。JVMが起動した後、任意のIDEやツールを使用してそのJVMに接続できます。

アプレットまたはJava Web Startアプリケーションを実行するJVMに引数を渡す方法の詳細は、「JREへのパラメータの受渡し」を参照してください。次に、コマンド行からJava Web Startアプリケーションに詳細情報を渡す方法の例を示します。

bash$ javaws -J-agentlib:jdwp=transport=dt_socket,address=4000,server=y,suspend=y http://acme.com/my/webstart.jnlp

このコードではエージェントが、JVMの初期化後に一時停止し、デバッガがポート4000上で接続してくるのを待つように指示されています。

- JavaアプレットおよびJava Web Startアプリケーションのプロファイリング:

スタンドアロンのJavaアプリケーションをプロファイリングするときに、任意のIDEはおそらく、JVMTIエージェントを使ってプログラムの実行に関する詳細情報を収集します。アプレットやJava Web Startアプリケーションでも同じことを行えますが、次の例に示すように、-agentpathオプションをJVMに渡してJVMTIエージェントを明示的に構成する必要がある場合があります。JVMにオプションを渡す方法の詳細は、「JREへのパラメータの受渡し」を参照してください。

set _JPI_VM_OPTIONS="-agentpath:C:\Tools\NetBeans\profiler\lib\deployed\jdk16\windows\profilerinterface.dll=C:\Tools\NetBeans\profiler\lib,5140"

次にブラウザを起動します。このブラウザ・セッションで実行されるすべてのアプレットで、NetBeansプロファイル・エージェントが有効になります。NetBeans IDEを使用してjavaプロセスに接続できます。使用する必要があるエージェントやその構成方法に関する具体的な詳細については、プロファイラのドキュメントを参照してください。

- メモリー・リークのデバッグ:

アプレットまたはアプリケーションを実行しているプロセス上のスタンドアロン・アプリケーションで使用できる手法については、「メモリー・リークのトラブルシューティング」参照してください。たとえば、jmapを使用したヒープ・ダンプの取得、jconsoleを使用したスレッドの監視、-XX:+HeapDumpOnOutOfMemoryErrorをJVMに渡して(「JREへのパラメータの受け渡し」を参照)エラー発生時にメモリー・ダンプの取得を実行します。jpsユーティリティを使用してアプリケーションを実行しているプロセスのプロセスIDを調べます。

第V部 バグ・レポートの提出

問題が引き続き発生するかどうかを確認するための最新の更新リリースによるテストに関する推奨事項。バグ・レポートの提出に関する指針、およびバグ・レポート用のデータを収集する方法の説明。

- バグ・レポートの提出

17 バグ・レポートの提出

バグ・レポートの提出方法に関する指針。これには、レポートを提出する前に試すこと、およびレポート用に収集するデータに関する提案が含まれています。

この章の構成は、次のとおりです。

- 更新リリースに含まれる修正のチェック

- バグ・レポートの提出の準備

- バグ・レポート用のデータの収集

- コア・ダンプの収集

更新リリースに含まれる修正のチェック

最新プラットフォームはJava SE 9です。このリリースへの定期的にスケジュールされた更新には、そのプラットフォームの初期リリース以降に確認された一連のクリティカルなバグの修正が含まれています。

更新リリースが利用可能になると、Java SEダウンロード・サイトにあるデフォルトのダウンロードになります。

このダウンロード・サイトには、そのリリースにおけるバグ修正のリストを示すリリース・ノートが含まれています。リストにある各バグは、バグ・データベース内のバグの説明にリンクされています。リリース・ノートには、以前の更新リリースにおける修正のリストも含まれています。問題が発生した場合やバグの疑いがある場合には、診断の初期段階として、最新の更新リリースで使用可能な修正のリストをチェックしてください。

ある問題が、すでに修正されたバグと同じものであるか明確でない場合があります。利用可能な最新の更新リリースでテストし、問題が引き続き発生するかどうか確認することを常にお薦めします。

バグ・レポートの提出の準備

バグ・レポートの提出方法に関する推奨手順。

バグ・レポートを提出する前に、次の推奨事項を検討してください。

- 最初に、最新の更新リリースでテストし、その問題が引き続き発生するかどうかを確認してください。古いリリースに対してバグ・レポートが提出されている場合は、利用可能な最新の更新リリースまたは利用可能な最新のアーリー・アクセス(EA)リリースでテストします。EAリリースには新機能やバグ修正が含まれている場合があります。

- できるだけ多くの関連データを収集します。たとえば、デッドロックが発生した場合はスレッド・ダンプを生成し、クラッシュが発生した場合はコア・ファイル(該当する場合)とhs_errファイルを見つけます。いずれの場合も、環境と、問題が発生する直前に実行されていたアクションを文書化することが重要です。

- 適用可能な場合は、元の状態を復元し、文書化した手順を使って問題を再現してみます。これにより、その問題が再現可能なものであるか、間欠的なものであるかを判断できます。

- 問題が再現可能な場合は、問題を絞り込んでみてください。場合によっては、小規模なスタンドアロンのテスト・ケースを使ってバグを再現できます。小規模なテスト・ケースで再現されるバグは通常、大規模で複雑なアプリケーションで構成されているテスト・ケースに比べて診断が容易です。

- バグ・データベースを検索して、このバグまたは同様のバグが報告されているかどうかを確認します。バグがすでに報告されている場合は、次のような詳細情報がバグ・レポートに記載されている可能性があります。

- バグがすでに修正されている場合、それが修正されたリリース。

- その問題の回避方法。

- バグが発生する環境についてさらに詳しく説明する、評価内のコメント。

- バグがすでに報告されていると判断した場合は、新しいバグを提出します。

バグを提出する前に、問題が発生した環境が、サポートされている構成であることを確認します。サポートされているシステム構成を参照してください。

システム構成のほかに、サポート対象のロケールのリストも確認します。サポート対象のロケールのWebページを参照してください。

Oracle Solarisの場合は、そのオペレーティング・システム用の推奨パッチ・クラスタを調べて、推奨パッチがインストールされていることを確認します。

バグ・レポート用のデータの収集

一般に、利用可能な最新の更新リリースまたは利用可能な最新のアーリー・アクセス(EA)リリースでテストして、問題が引き続き発生するかどうかを確認してから、できるだけ多くの関連データを収集して、バグ・レポートを作成するかサポート・コールを行うことをお薦めします。

次の各セクションでは、収集する必要があるデータを提案し、該当する場合は、データを取得するためのコマンドや一般的な手順に関する推奨事項を説明します。

- ハードウェアの詳細

- オペレーティング・システムの詳細

- Java SEのバージョン

- コマンド行オプション

- 環境変数

- 致命的エラー・ログ

- コア・ダンプおよびクラッシュ・ダンプ

- 問題の詳細な説明

- ログおよびトレース

- トラブルシューティング手順からの結果

ハードウェアの詳細

致命的なエラーが発生すると、エラー・ログにハードウェアの詳細が保存されます。

特定のハードウェア構成でのみバグが発生したり再現できたりすることがあります。致命的なエラーが発生した場合は、エラー・ログにハードウェアの詳細が含まれている可能性があります。エラー・ログを入手できない場合は、マシンのプロセッサの番号と種類、クロック速度、および該当する場合かつ既知の場合は、そのプロセッサの機能に関するいくつかの詳細をバグ・レポートにまとめます。たとえばIntelプロセッサの場合、ハイパースレッディングが使用可能であることがそれに該当します。

オペレーティング・システムの詳細

オペレーティング・システムの詳細を取得するために使用できるコマンド。

Oracle Solarisオペレーティング・システムでは、showrev -aコマンドはオペレーティング・システムのバージョンおよびパッチ情報を出力します。

Linuxでは、使用されているディストリビューションとバージョンを知ることが重要です。/etc/*releaseファイルがリリース情報を示している場合がありますが、コンポーネントとパッケージは個別にアップグレードできるため、必ずしもそれが信頼できる構成を示しているとはかぎりません。そのため、*releaseファイルからの情報に加えて、次の情報も収集してください。

- カーネルのバージョン。これはuname -aコマンドを使って取得できます。

- glibcのバージョン。rpm -q glibcコマンドは、glibcのパッチ・レベルを示します。

- スレッド・ライブラリ。Linuxには、LinuxThreadsおよびNPTLという2つのスレッド・ライブラリがあります。LinuxThreadsライブラリは2.4以前のカーネルで使用され、固定スタックと浮動スタックのバリアントがあります。Native POSIX Thread Library (NPTL)は2.6カーネルで使用されます。一部のLinuxリリース(RHEL3など)には、NPTLの2.4カーネルへのバックポートが含まれています。使用されているスレッド・ライブラリを確認するには、コマンドgetconf GNU_LIBPTHREAD_VERSIONを使用します。getconfコマンドが、その変数が存在しないことを示すエラーを返す場合は、古いカーネルをLinuxThreadsライブラリとともに使用している可能性があります。

Java SEのバージョン

Java SEのバージョン文字列は、java -versionコマンドを使用すると取得できます。

同じマシン上にJava SEのバージョンが複数インストールされている可能性があります。したがって、適切なバージョンのjavaコマンドが間違いなく使用されるように、そのインストールのbinディレクトリがPATH環境変数内で他のインストールよりも前にあることを確認してください。

コマンド行オプション

バグ・レポートに致命的エラー・ログが含まれていない場合は、コマンド行全体とそのすべてのオプションを文書化することが重要です。これには、ヒープ設定を指定するオプション(-mxオプションなど)やHotSpot固有のオプションを指定する-XXオプションが含まれます。

Java SEの機能の1つに、ガベージ・コレクタ・エルゴノミクスがあります。サーバークラス・マシンでは、javaコマンドはHotSpot Server VMとパラレル・ガベージ・コレクタを起動します。マシンがサーバー・マシンと見なされるのは、少なくとも2基のプロセッサと2Gバイト以上のメモリーを備えている場合です。

-XX:+PrintCommandLineFlagsオプションを使用すると、コマンド行オプションを検証できます。このオプションは、コマンド行のVMに対するすべてのフラグを出力します。jmapユーティリティを使用すると、実行中のVMまたはコア・ファイルのコマンド行オプションを取得することもできます。

環境変数

環境変数の設定が原因で問題が発生する場合があります。バグ・レポートの作成時に、次のJava環境変数の値(設定されている場合)を記入してください。

- JAVA_HOME

- JRE_HOME

- JAVA_TOOL_OPTIONS

- _JAVA_OPTIONS

- CLASSPATH

- JAVA_COMPILER

- PATH

- USERNAME

また、次のオペレーティング・システム固有の環境変数も収集してください。

- Oracle SolarisおよびLinuxオペレーティング・システムでは、次の環境変数の値を収集します。

- LD_LIBRARY_PATH

- LD_PRELOAD

- SHELL

- DISPLAY

- HOSTTYPE

- OSTYPE

- ARCH

- MACHTYPE

- Linuxでは、次の環境変数の値も収集します。

- LD_ASSUME_KERNEL

- _JAVA_SR_SIGNUM

- Windowsでは、次の環境変数の値を収集します。

- OS

- PROCESSOR_IDENTIFIER

- _ALT_JAVA_HOME_DIR

致命的エラー・ログ

致命的エラー・ログは、致命的エラーの発生時に作成されます。

最新の更新リリースでテストし、問題が引き続き発生するかどうか確認することをお薦めします。

致命的エラーが発生すると、エラー・ログが作成されます。「致命的エラー・ログ」を参照してください。

このエラー・ログには、致命的エラーの発生時に得られた情報(バージョンや環境の情報、クラッシュを引き起こしたスレッドの詳細など)が含まれています。

致命的エラー・ログが生成される場合は、必ずそれをバグ・レポートに含めるか、サポート・コール時に報告してください。

コア・ダンプおよびクラッシュ・ダンプ

コア・ダンプやクラッシュ・ダンプは、システム・クラッシュまたはハングアップ・プロセスの診断を試みる際に非常に役立つ可能性があります。

ダンプの生成手順は、「コア・ダンプの収集」で説明されています。

問題の詳細な説明

問題の説明を作成するときは、できるだけ多くの関連情報を含めるようにしてください。

アプリケーション、環境、そして何よりも問題が発生した時点につながるイベントを記述します。

複雑なアプリケーション環境でしか問題を再現できない場合があります。その場合は、ログ、コア・ファイル、およびその他の関連情報を伴う説明が問題を診断する唯一の手段になる可能性があります。このような状況では、提出者が問題の発生しているシステムでさらなる診断を行ったり、テスト・バイナリを実行したりする意志があるかどうかをその説明で示すようにしてください。

- 問題が再現可能な場合は、その問題を再現するために必要な手順の一覧を示します。

- 問題を小さなテスト・ケースで再現できる場合は、そのテスト・ケースと、そのテスト・ケースをコンパイルして実行するためのコマンドを含めます。

- テスト・ケースまたは問題にサード・パーティのコード(商用またはオープン・ソースのライブラリやパッケージなど)が必要な場合は、そのライブラリの場所と入手方法についての詳細を提供します。

ログおよびトレース

ログまたはトレース出力が問題の原因を迅速に特定するのに役立つ可能性があります。

たとえば、パフォーマンスの問題の場合、-verbose:gcオプションの出力が問題の診断に役立つ可能性があります。(これは、ガベージ・コレクタからの出力を有効にするオプションです。)

別のケースでは、jstatコマンドからの出力を使用すると、問題の発生に至るまでの期間にわたる統計情報を取得できます。

VMのデッドロックまたはハング・アップ(ループが原因など),の場合は、スレッド・スタックが問題の診断に役立つ可能性があります。スレッド・スタックを取得するには、Oracle SolarisおよびLinuxでは[Ctrl]+[\]、Windowsでは[Ctrl]+[Break]を押します。

通常は、関連のあるすべてのログ、トレース、およびその他の出力をバグ・レポートまたはサポート・コール時に提供します。

トラブルシューティング手順の結果

すでに行ったすべてのトラブルシューティングの手順と結果を報告します

前提条件: バグ・レポートを提出する前に、必ず、実行したトラブルシューティング手順をすべて文書化してください。

たとえば、問題がクラッシュであり、アプリケーションにネイティブ・ライブラリが含まれている場合は、バグがネイティブ・コード内にある可能性を減らすために、すでに-Xcheck:jniオプションを使用してアプリケーションを実行している場合があります。別のケースとして、HotSpot Server VM (-serverオプション)で発生するクラッシュが考えられます。HotSpot Client VM (-clientオプション)でもテストを行い、問題が発生しない場合は、バグがHotSpot Server VMに固有のものである可能性があることの目安になります。

一般に、すでに行われたトラブルシューティング手順と結果をすべてバグ・レポートに含めます。この種の情報によって、問題の診断に要する時間が短縮されることが頻繁にあります。

コア・ダンプの収集

コア・ダンプ(クラッシュ・ダンプとも呼ばれる)の生成および収集の手順。コア・ダンプまたはクラッシュ・ダンプとは、実行中のプロセスのメモリー・スナップショットです。

コア・ダンプは、致命的エラーまたは処理できないエラー(シグナルまたはシステム例外など)の発生時にオペレーティング・システムによって自動的に作成されることがあります。また、コア・ダンプはシステム提供のコマンド行ユーティリティを使用して強制的に取得することもできます。ハングアップしているように見えるプロセスを診断する際にコア・ダンプが役立つ場合があります。つまり、コア・ダンプによってハングアップの原因に関する情報が明らかになることがあります。

コア・ダンプを収集する場合は、コア・ファイルを解析できるように、必ず環境に関するその他の情報(OSバージョン、パッチ情報、致命的エラー・ログなど)も収集してください。

コア・ダンプには通常、クラッシュまたはハング・アップしたプロセスのメモリー・ページがすべて含まれているとはかぎりません。ここで取り上げている各オペレーティング・システムでは、プロセスのテキスト(またはコード)ページはコア・ダンプに含まれません。しかし、少なくともヒープとスタックのページがコア・ダンプに含まれていないと役に立ちません。クラッシュのポストモーテム解析には、切り詰められていない完全なコア・ダンプ・ファイルの収集が不可欠です。

次の項では、コア・ダンプの収集シナリオについて説明します。

- Oracle Solarisでのコア・ダンプの収集

- Linuxでのコア・ダンプの収集

- コア・ファイルを取得しない理由

- Windowsでのクラッシュ・ダンプの収集

Oracle Solarisでのコア・ダンプの収集

Oracle Solarisオペレーティング・システムでは、セグメンテーション違反や不正な命令などの処理できないシグナルによってコア・ダンプが作成されます。

デフォルトでは、コア・ダンプはプロセスの現在の作業ディレクトリに作成され、コア・ダンプ・ファイルの名前はcoreになります。ユーザーは、コア・ファイル管理ユーティリティcoreadmを使用すると、そのコア・ダンプの場所と名前を構成できます。この手順については、coreadmユーティリティのマニュアル・ページに詳しく説明されています。

ulimitユーティリティは、現在のシェルとその子孫で利用できるシステム・リソースへの制限を設定または取得するために使用されます。コア・ファイルのサイズ制限をチェックまたは設定するには、ulimit -cコマンドを使用します。その制限が必ずunlimitedに設定されるようにしてください。そうしないと、コア・ファイルが切り詰められる可能性があります。

注意:

ulimitはBashシェルの組込みコマンドです。Cシェルでは、limitコマンドを使用してください。

VMまたはアプリケーションの起動に使用されるスクリプトがコア・ダンプの作成を無効にしないようにしてください。

gcoreユーティリティを使用すると、実行中のプロセスのコア・イメージを取得できます。このユーティリティは、コア・ダンプを強制的に作成させるプロセスのプロセスID (pid)を受け入れます。

マシン上で実行されているJavaプロセスのリストを取得するには、次のいずれかのコマンドを使用できます。

- ps -ef | grep java

- pgrep java

- jps

注意:

jpsコマンド行ユーティリティでは、名前の照合(つまり、プロセスのコマンド名の中に「java」がないか検索する)を行わないため、Javaプロセスだけでなく、Java VMの埋込みプロセスも一覧表示できます。

次は、Oracle Solarisでコア・ダンプを収集するための2つの方法です。

- Oracle SolarisでのShowMessageBoxOnErrorオプション:
Javaプロセスは、-XX:+ShowMessageBoxOnErrorコマンド行オプションを使って起動できます。致命的エラーが発生すると、そのプロセスは標準エラーにメッセージを出力し、標準入力からの「yes」または「no」の応答を待ちます。次の例は、予期しないシグナルが発生した場合の出力を示しています。

=======================================================================
Unexpected Error
-----------------------------------------------------------------------
SIGSEGV (0xb) at pc=0xfeba31ac, pid=8677, tid=2
Do you want to debug the problem?
To debug, run 'dbx - 8677'; then switch to thread 2
Enter 'yes' to launch dbx automatically (PATH must include dbx)
Otherwise, press RETURN to abort...
=======================================================================

「yes」と応答するか、[Return]([Enter])キーを押す前に、gcoreユーティリティを使用してコア・ダンプを強制します。その後、「yes」と入力してdbxデバッガを起動できます。

- trussユーティリティによるプロセスの中断:
-XX:+ShowMessageBoxOnErrorオプションを指定できない場合は、trussユーティリティを使用できる可能性があります。このOracle Solarisオペレーティング・システムのユーティリティは、システム・コールおよびシグナルをトレースするために使用されます。このユーティリティを使用すると、特定の関数またはシステム・コールに達した時点でプロセスを中断できます。

次の例のコマンドは、trussユーティリティを使用して、exitシステム・コールが実行される(つまり、プロセスが終了しようとしている)ときにプロセスを中断する方法を示しています。

$ truss -t \!all -s \!all -T exit -p pid

プロセスがexitを呼び出すと、そのプロセスが中断されます。この時点で、デバッガをプロセスに接続することも、gcoreを呼び出してコア・ダンプを強制することもできます。

Linuxでのコア・ダンプの収集

Linuxオペレーティング・システムでは、セグメンテーション違反や不正な命令などの処理できないシグナルによってコア・ダンプが作成されます。

デフォルトでは、コア・ダンプはプロセスの現在の作業ディレクトリに作成され、コア・ダンプ・ファイルの名前はcore.pidになります(pidはクラッシュしたJavaプロセスのプロセスID)。

ulimitユーティリティは、現在のシェルとその子孫で利用できるシステム・リソースへの制限を設定または取得するために使用されます。コア・ファイルのサイズ制限をチェックまたは設定するには、ulimit -cコマンドを使用します。その制限が必ずunlimitedに設定されるようにしてください。そうしないと、コア・ファイルが切り詰められる可能性があります。

注意:

ulimitはBashシェルの組込みコマンドです。Cシェルでは、limitコマンドを使用してください。

VMまたはアプリケーションの起動に使用されるスクリプトがコア・ダンプの作成を無効にしないようにしてください。

gcoreコマンドをgdb (GNUデバッガ)インタフェースで使用すると、実行中のプロセスのコア・イメージを取得できます。このユーティリティは、コア・ダンプを強制的に作成させるプロセスのpidを受け入れます。

マシン上で実行されているJavaプロセスのリストを取得するには、次のいずれかのコマンドを使用できます。

- ps -ef | grep java

- pgrep java

- jps

注意:

jpsコマンド行ユーティリティでは、名前の照合(つまり、プロセスのコマンド名の中に「java」がないか検索する)を行わないため、Javaプロセスだけでなく、Java VMの埋込みプロセスも一覧表示できます。

次は、Linuxでコア・ダンプを収集するための1つのオプションです。

- LinuxでのShowMessageBoxOnErrorオプション:
Javaプロセスは、-XX:+ShowMessageBoxOnErrorコマンド行オプションを使って起動できます。致命的エラーが発生すると、そのプロセスは標準エラーにメッセージを出力し、標準入力からの「yes」または「no」の応答を待ちます。次の例は、予期しないシグナルが発生した場合の出力を示しています。

=======================================================================
Unexpected Error
-----------------------------------------------------------------------
SIGSEGV (0xb) at pc=0x06232e5f, pid=11185, tid=8194
Do you want to debug the problem?
To debug, run 'gdb /proc/11185/exe 11185'; then switch to thread 8194
Enter 'yes' to launch gdb automatically (PATH must include gdb)
Otherwise, press RETURN to abort...
=======================================================================

示されたエラー・レポートで示唆されているように、gdb (GNUデバッガ)インタフェースを起動するには、「yes」と入力します。gdbプロンプトで、gcoreコマンドを指定できます。このコマンドは、デバッグされたプロセスのコア・ダンプをcore.pidという名前で作成します(pidはクラッシュしたプロセスのプロセスID)。使用しているgdbバージョンでgdb gcoreコマンドがサポートされていることを確認してください。gdbコマンド・プロンプトで、help gcoreを確認してください。

コア・ファイルを取得しない理由

コア・ファイルが生成されない場合がある理由のリスト。

特に断りがないかぎり、このリストはOracle SolarisとLinuxオペレーティング・システムの両方に関連します。

- ユーザーに、そのプロセスの現在の作業ディレクトリへの書込み権がありません。

- ユーザーには現在の作業ディレクトリへの書込み権がありますが、読取り専用の権限を持つcoreという名前のファイルがすでに存在しています。

- 現在のディレクトリに十分な空き領域がないか、空き領域がまったく残っていません。

- 現在のディレクトリに、coreという名前のサブディレクトリがあります。

- 現在の作業ディレクトリがリモートです。ネットワーク・ファイル・システム(NFS)によってマップされている可能性があり、コア・ダンプが作成されようとしているときにNFSが失敗しました。

- Oracle Solarisオペレーティング・システムのみ: coreadmツールを使用してコア・ファイルのディレクトリと名前が構成されましたが、構成されたディレクトリに前述の1つ以上の理由が当てはまります。

- コア・ファイルのサイズ制限が低すぎます。ulimit -cコマンド(Bashシェル)またはlimit -cコマンド(Cシェル)を使用して、コア・ファイル・サイズの制限をチェックしてください。このコマンドからの出力がunlimitedでない場合、コア・ダンプのファイル・サイズが十分な大きさでない可能性があります。その場合は、切り詰められたコア・ダンプが作成されるか、コア・ダンプがまったく作成されません。また、VMまたはアプリケーションの起動に使用されるスクリプトがコア・ダンプの作成を無効にしないようにしてください。

- プロセスがsetuidプログラムを実行しているため、オペレーティング・システムは、明示的に構成されないかぎり、コアをダンプしません。

- Java固有: プロセスがSIGSEGVまたはSIGILLを受信してもコアがダンプされなかった場合、プロセスがそれを処理した可能性があります。たとえば、HotSpot VMはSIGSEGVシグナルを、正当な目的(NullPointerExceptionのスローや最適化解除など)のために使用します。Java VMによってシグナルが処理されないのは、現在の命令(PC)がJava VM生成コードから外れる場合だけです。これらが、HotSpotでコアがダンプされる唯一の場合になります。

- Java固有: VMの作成に、JNI呼び出しAPIが使用されました。標準のJavaランチャは使用されませんでした。カスタムJavaランチャ・プログラムが、シグナルを消費してその処理をすませ、ログ・エントリを暗黙のうちに生成しました。この状況は、特定のアプリケーション・サーバーやWebサーバーで発生しました。これらのJava VM埋め込みプログラムは、異常終了後にシステムの再起動(フェイルオーバー)を透過的に試行します。この場合、コア・ダンプが生成されないという事実は、機能であってバグではありません。

Windowsでのクラッシュ・ダンプの収集

Windowsオペレーティング・システムでは、ワトソン博士ログ・ファイル、ユーザー・ミニダンプおよびワトソン博士フル・ダンプの3種類のクラッシュ・ダンプがあります。

- ワトソン博士のログ・ファイル。これは、障害時のスタック・トレースとその他のいくつかの詳細を含むテキスト・エラー・ログ・ファイルです。

- ユーザーのミニダンプ。これは部分的なコア・ダンプと見なされます。このダンプにはプロセスの有用なメモリー・ページがすべて含まれているわけではないため、完全なコア・ダンプではありません。

- ワトソン博士の完全ダンプ。これは、UNIXコア・ダンプと同等のものです。このダンプには、プロセスのほとんどのメモリー・ページが含まれています(コード・ページを除く)。

Windowsで予期しない例外が発生した場合、実行されるアクションは次のレジストリ・キーの2つの値によって異なります。

\\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug

それらの2つの値には、DebuggerおよびAutoという名前が付いています。Auto値は、アプリケーション・エラーが発生したときに、Debuggerエントリの値で指定されたデバッガが自動的に起動するかどうかを示します。

- Auto値が0の場合は、アプリケーション・エラーが発生したときにシステムがメッセージ・ボックスを表示してユーザーに知らせることを示します。

- Auto値が1の場合は、デバッガが自動的に起動することを示します。

Debuggerの値は、プログラム・エラーのデバッグに使用されるデバッガ・コマンドです。

Windowsでは、プログラム・エラーが発生すると、Auto値を調べて、その値が0の場合に、Debugger値に含まれるコマンドを実行します。Debuggerの値が有効なコマンドである場合は、「OK」と「キャンセル」の2つのボタンを含むメッセージ・ボックスが作成されます。ユーザーが「OK」をクリックすると、プログラムが終了します。ユーザーが「キャンセル」をクリックすると、指定されたデバッガが開始されます。Autoエントリの値が1に設定され、Debuggerエントリの値が有効なデバッガを表すコマンドを指定している場合、システムはそのデバッガを自動的に起動し、メッセージ・ボックスを生成しません。

次は、Windowsでクラッシュ・ダンプを収集するための2つの方法です。

- ワトソン博士の構成:

ワトソン博士のデバッガは、クラッシュ・ダンプ・ファイルの作成に使用されます。デフォルトでは、ワトソン博士のデバッガ(drwtsn32.exe)はWindowsシステム・フォルダ(%SystemRoot%\System32)にインストールされます。

ワトソン博士をポストモーテム・デバッガとしてインストールするには、次のコマンドを実行します。

drwtsn32 -i

クラッシュ・ダンプ・ファイルの名前と場所を構成するには、オプションを付けずにdrwtsn32を実行します。

ワトソン博士のGUIウィンドウで、「クラッシュ・ダンプ・ファイルの作成」チェック・ボックスが選択されていること、およびクラッシュ・ダンプ・ファイル・パスとログ・ファイル・パスがそれぞれのテキスト・フィールドに構成されていることを確認します。

レジストリを使用すると、フル・ダンプを作成するようにワトソン博士を構成できます。レジストリ・キーを次の例に示します。

System Key: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DrWatson]
Entry Name: CreateCrashDump
Value: (0 = disabled, 1 = enabled)

注意:
アプリケーションが例外を処理する場合は、レジストリで構成されたデバッガは呼び出されません。その場合は、-XX:+ShowMessageBoxOnErrorコマンド行オプションを使用することで、致命的エラーの状態ではユーザーが介入するまで待つようにプロセスを強制するのが適切である可能性があります。

- クラッシュ・ダンプの強制:

Windowsオペレーティング・システムでは、userdumpコマンド行ユーティリティを使って実行中のプロセスのDr. Watsonダンプを強制できます。userdumpユーティリティはWindowsに同梱されていません。OEM Support Toolsパッケージのコンポーネントとして提供されています。

クラッシュ・ダンプを強制する別の方法は、windbgデバッガを使用することです。windbgを使用することの主な利点は、非侵入的な方法(つまり読取り専用)でプロセスに接続できることです。通常、Windowsはクラッシュ・ダンプの取得後にプロセスを終了させますが、非侵入型の接続ではクラッシュ・ダンプの取得後もプロセスを続行できます。デバッガを接続するには、「Attach to Process」オプションを選択し、「Noninvasive」チェック・ボックスを選択します。

デバッガが接続されたら、次の例に示されたコマンドを使用してクラッシュ・ダンプを取得できます。

.dump /f crash.dmp

windbgデバッガは、Debugging Tools for Windowsのダウンロードに含まれています。

このダウンロードに含まれる追加のdumpchk.exeユーティリティは、メモリー・ダンプ・ファイルが正しく作成されたことを検証できます。

userdump.exeとwindbgのどちらにも、プロセスのpidが必要です。userdump -pコマンドは、すべてのプロセスのプロセスとプログラムを一覧表示します。これは、アプリケーションがjava.exeランチャで起動されることがわかっている場合に役立ちます。ただし、カスタム・ランチャが使用される場合(埋込みVM)、プロセスの認識が困難である可能性があります。その場合は、JavaプロセスのPIDのみを一覧表示するjpsコマンド行ユーティリティを使用できます。

Oracle SolarisおよびLinuxオペレーティング・システムと同様に、Windowsでも-XX:+ShowMessageBoxOnErrorコマンド行オプションを使用できます。致命的エラーが発生すると、プロセスはメッセージ・ボックスを表示し、ユーザーからのyesまたはnoの応答を待ちます。

「はい」または「いいえ」をクリックする前に、userdump.exeユーティリティを使用して、そのJavaプロセスのワトソン博士のダンプを生成できます。このユーティリティは、プロセスがハングアップしているように見える場合にも使用できます。

付録

この部の内容は次のとおりです。

- 致命的エラー・ログ
致命的エラー・ログの内容と場所について説明します

- Java 2Dのプロパティ
Java 2Dに関する問題のトラブルシューティングに役立つプロパティについて説明します

- 環境変数とシステム・プロパティ
Java HotSpot Server VMに関する問題をトラブルシューティングする際に役立つ環境変数とシステム・プロパティについて説明します

- コマンド行オプション
Java HotSpot Server VMに関する問題を診断する際に役立つコマンド行オプションについて説明します

- このリリースのツールのサマリー
JDKの最新リリースと以前のリリースで利用可能なツールのサマリーを説明します。

A 致命的エラー・ログ

致命的エラー・ログ、およびその場所と内容について説明します。

致命的エラー・ログは、致命的エラーの発生時に作成されます。それには、致命的エラーの発生時に取得された情報と状態が含まれています。

注意:

更新リリースではこのファイルの形式が若干変わる可能性があります。

この付録の内容は次のとおりです。

- 致命的エラー・ログの場所

- 致命的エラー・ログの説明

- ヘッダー形式

- スレッド・セクションの形式

- プロセス・セクションの形式

- システム・セクションの形式

致命的エラー・ログの場所

このログ・ファイルが作成される場所を指定するには、-XX:ErrorFile=fileという製品フラグ(fileはそのログ・ファイルの場所を表すフル・パス)を使用します。

file変数内の%%という部分文字列は%に変換され、%pという部分文字列はプロセスのPIDに変換されます。

次の例では、エラー・ログ・ファイルが/var/log/javaディレクトリに書き込まれ、java_errorpid.logという名前が付けられます。

java -XX:ErrorFile=/var/log/java/java_error%p.log

-XX:ErrorFile=fileフラグを指定しない場合、デフォルトのログ・ファイル名はhs_err_pid.log ( pidはプロセスのPID)になります。

また、-XX:ErrorFile=fileフラグを指定しない場合、システムはそのファイルをプロセスの作業ディレクトリに作成しようとします。作業ディレクトリにファイルを作成できない場合(領域不足、アクセス権の問題、またはその他の問題がある場合)は、オペレーティング・システムの一時ディレクトリにファイルが作成されます。Oracle SolarisおよびLinuxオペレーティング・システムの一時ディレクトリは/tmpです。Windowsでは、一時ディレクトリは環境変数TMPの値で指定されます。この環境変数が指定されていないと、TEMP環境変数の値が使用されます。

致命的エラー・ログの説明

致命的エラー・ログの説明および致命的エラーの発生時に取得された情報について説明する項。

エラー・ログには、可能であれば次の情報を含む、致命的エラーの発生時に取得された情報が含まれています。

- 致命的エラーを引き起こした操作例外またはシグナル

- バージョンおよび構成情報

- 致命的エラーを引き起こしたスレッドの詳細およびスレッドのスタック・トレース

- 実行中のスレッドとその状態のリスト

- ヒープに関するサマリー情報

- ロードされているネイティブ・ライブラリのリスト

- コマンド行引数

- 環境変数

- オペレーティング・システムおよびCPUの詳細

注意:

この情報の一部しかエラー・ログに出力されない場合もあります。これが起こる可能性があるのは、致命的エラーの深刻度が高いため、エラー・ハンドラが一部の詳細情報しか復元および報告できない場合です。

エラー・ログは、次のセクションで構成されるテキスト・ファイルです。

- クラッシュについて簡単に説明するヘッダー。「ヘッダー形式」を参照してください。

- スレッド情報を含むセクション。「スレッド・セクションの形式」を参照してください。

- プロセス情報を含むセクション。「プロセス・セクションの形式」を参照してください。

- システム情報を含むセクション。「システム・セクションの形式」を参照してください。

注意:

ここで説明している致命的エラー・ログの形式はJava SE 6に基づいています。その他のリリースでは形式が異なる場合があります。

ヘッダー形式

すべての致命的エラー・ログ・ファイルの先頭にあるヘッダー・セクションには、問題の簡易説明が含まれます。

このヘッダーは、標準出力にも出力されるほか、アプリケーションの出力ログに現れる可能性もあります。

ヘッダーにはHotSpot仮想マシン・エラー報告ページへのリンクが含まれていますが、そのページではユーザーがバグ・レポートを提出できます。

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f0f159f857d, pid=18240, tid=18245
#
# JRE version: Java(TM) SE Runtime Environment (9.0+167) (build 9-ea+167)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (9-ea+167, mixed mode, tiered, compressed oops, g1 gc, linux-amd64)
# Problematic frame:
# C  [libMyApp.so+0x57d]  Java_MyApp_readData+0x11
#
# Core dump will be written. Default location: /cores/core.18240)
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

この例は、予期しないシグナルによってVMがクラッシュしたことを示しています。

次の例に示すように、次の行には、シグナルのタイプ、シグナルの原因となったプログラム・カウンタ(pc)、プロセスID、およびスレッドIDが記述されています。

#  SIGSEGV (0xb) at pc=0x00007f0f159f857d, pid=18240, tid=18245
|      |           |                    |         +--- thread id
|      |           |                    +------------- process id
|      |           +--------------------------- program counter
|      |                                        (instruction pointer)
|      +--------------------------------------- signal number
+---------------------------------------------- signal name

次の例に示すように、次の行には、VMのバージョン(Client VMまたはServer VM)、アプリケーションが混合モード、インタプリタ・モードのどちらで実行されたかを示す情報、およびクラス・ファイル共有が有効かどうかを示す情報が含まれます。

# Java VM: Java HotSpot(TM) 64-Bit Server VM (9-ea+167, mixed mode, tiered, compressed oops, g1 gc, linux-amd64)

次の例に示すように、その次の情報は、クラッシュの原因となった関数フレームです。

# Problematic frame:
# C  [libMyApp.so+0x57d]  Java_MyApp_readData+0x11
|              +-- Same as pc, but represented as library name and offset.
|                  For position-independent libraries (JVM and most shared
|                  libraries), it is possible to inspect the instructions
|                  that caused the crash without a debugger or core file
|                  by using a disassembler to dump instructions near the
|                  offset.
+----------------- Frame type

この例のフレーム・タイプ「C」は、ネイティブCフレームを示しています。表A-1は、可能なフレーム・タイプを示しています。

表A-1 フレーム・タイプ

フレーム・タイプ

説明

C

ネイティブCフレーム

j

解釈済Javaフレーム

V

VMフレーム

v

VM生成のスタブ・フレーム

J

その他のフレーム・タイプ(コンパイル済みJavaフレームなど)

内部エラーが発生した場合も、VMエラー・ハンドラによって似たようなエラー・ダンプが生成されます。ただし、ヘッダー形式が異なります。内部エラーの例としては、guarantee()の失敗、アサーションの失敗、ShouldNotReachHere()などが挙げられます。次の例は、内部エラーのヘッダー形式を示しています。

#
# An unexpected error has been detected by HotSpot Virtual Machine:
#
# Internal Error (4F533F4C494E55583F491418160E43505000F5), pid=10226, tid=16384
#
# Java VM: Java HotSpot(TM) Client VM (1.6.0-rc-b63 mixed mode)

上のヘッダーにはシグナル名やシグナル番号がありません。2行目にはかわりに、Internal Errorと長い16進文字列が含まれるようになっています。この16進文字列は、エラーが検出されたソース・モジュールと行番号をエンコードしたものです。この「エラー文字列」は一般に、HotSpot仮想マシンを操作するエンジニアにしか役に立ちません。

エラー文字列は行番号をエンコードしているため、コード変更やリリースのたびに変わります。あるリリース(1.6.0など)の特定のエラー文字列を含むクラッシュは、(文字列が一致する場合でも)更新リリース(1.6.0_01など)の同じクラッシュに対応しない可能性があります。

注意:

特定のエラー文字列に関連する1つの状況で機能した回避方法や解決方法は、その同じエラー文字列に関連する別の状況でも機能すると考えないでください。次の事実に注意してください。

- エラーの根本的原因が同じでも、エラー文字列は異なることがあります。

- エラーのエラー文字列が同じでも、根本的原因は完全に異なることがあります。

したがって、バグのトラブルシューティングを行う際に、エラー文字列を唯一の基準として使用すべきではありません。

スレッド・セクションの形式

クラッシュしたスレッドに関する情報。

複数のスレッドが同時にクラッシュした場合、1つのスレッドのみが出力されます。

スレッド情報

スレッド・セクションの最初の部分は、次の例に示すように、致命的エラーを起こしたスレッドを示しています。

Current thread (0x00007f102c013000):  JavaThread "main" [_thread_in_native, id=18245, stack(0x00007f10345c0000,0x00007f10346c0000)]
|                      |         |            |             |             + stack
|                      |         |            |             +------------------ ID
|                      |         |            +------------------------------- state
|                      |         +-------------------------------------------- name
|                      +------------------------------------------------------ type
+----------------------------------------------------------------------------- pointer

スレッド・ポインタは、Java VM内部のスレッド構造体へのポインタです。ライブJava VMやコア・ファイルのデバッグを行なっているのでないかぎり、これは一般に役に立ちません。

次の一覧は、可能性のあるスレッド・タイプを示したものです。

- JavaThread

- VMThread

- CompilerThread

- GCTaskThread

- WatcherThread

- ConcurrentMarkSweepThread

表A-2は、重要なスレッド状態を示しています。

表A-2 スレッドの状態

スレッド状態

説明

_thread_uninitialized

スレッドが作成されていません。これが発生するのは、メモリーが破損した場合だけです。

_thread_new

スレッドは作成済ですがまだ開始されていません。

_thread_in_native

スレッドはネイティブ・コードを実行しています。エラーはおそらくネイティブ・コードのバグです。

_thread_in_vm

スレッドはVMコードを実行しています。

_thread_in_Java

スレッドは、解釈済みまたはコンパイル済みのJavaコードを実行しています。

_thread_blocked

スレッドがブロックされています。

..._trans

前のいずれかの状態の後に_transという文字列がある場合、スレッドが別の状態へ遷移中であることを意味します。

出力に含まれるスレッドIDは、ネイティブ・スレッド識別子です。

Javaスレッドがデーモン・スレッドの場合、スレッドの状態の前にdaemonという文字列が出力されます。

シグナル情報

エラー・ログ内のその次の情報は、VMが終了する原因となった予期しないシグナルの説明です。Windowsシステムでは、出力は次の例のように表示されます。

siginfo: ExceptionCode=0xc0000005, reading address 0xd8ffecf1

前述の例では、例外コードは0xc0000005 (ACCESS_VIOLATION)であり、スレッドがアドレス0xd8ffecf1を読み取ろうとしたときに例外が発生しました。

Oracle SolarisおよびLinuxオペレーティング・システムでは次のように、例外を識別するためにシグナル番号(si_signo)とシグナル・コード(si_code)が使用されます。

siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000000000

レジスタ・コンテキスト

エラー・ログ内のその次の情報は、致命的エラー発生時のレジスタ・コンテキストを示します。この出力の実際の形式はプロセッサに依存します。次の例は、Intel(R) Xeon(R)プロセッサの出力を示したものです。

Registers:
RAX=0x0000000000000000, RBX=0x00007f0f17aff3b0, RCX=0x0000000000000001, RDX=0x00007f1033880358
RSP=0x00007f10346be930, RBP=0x00007f10346be930, RSI=0x00007f10346be9a0, RDI=0x00007f102c013218
R8 =0x00007f0f17aff3b0, R9 =0x0000000000000008, R10=0x00007f1011bb1de9, R11=0x0000000101cfc5e0
R12=0x0000000000000000, R13=0x00007f0f17aff3b0, R14=0x00007f10346be9a8, R15=0x00007f102c013000
RIP=0x00007f0f159f857d, EFLAGS=0x0000000000010283, CSGSFS=0x0000000000000033, ERR=0x0000000000000004

レジスタの値は、次に説明する命令と組み合わせると、役立つ可能性があります。

機械命令

レジスタ値の後、次の例に示すように、エラー・ログには、システムがクラッシュしたときのスタックの最上段と、プログラム・カウンタ(PC)付近の32バイトの命令(操作コード)が含まれています。これらの操作コードを逆アセンブラでデコードすれば、クラッシュの場所の近くにあった命令を生成できます。注意: IA32およびAMD64命令は可変長であるため、常にクラッシュPCの前の命令を間違いなくデコードできるとはかぎりません。

Top of Stack: (sp=0x00007f10346be930)
0x00007f10346be930:   00007f10346be990 00007f1011bb1e15
0x00007f10346be940:   00007f1011bb1b33 00007f10346be948
0x00007f10346be950:   00007f0f17aff3b0 00007f10346be9a8
0x00007f10346be960:   00007f0f17aff5a0 0000000000000000

Instructions: (pc=0x00007f0f159f857d)
0x00007f0f159f855d:   3d e6 08 20 00 ff e0 0f 1f 40 00 5d c3 90 90 55
0x00007f0f159f856d:   48 89 e5 48 89 7d f8 48 89 75 f0 b8 00 00 00 00
0x00007f0f159f857d:   8b 00 5d c3 90 90 90 90 90 90 90 90 90 90 90 90
0x00007f0f159f858d:   90 90 90 55 48 89 e5 53 48 83 ec 08 48 8b 05 88

スレッド・スタック

次の例に示すように、可能な場合、エラー・ログ内のその次の出力はスレッド・スタックです。これには、スタックのベースと先頭のアドレス、現在のスタック・ポインタ、およびスレッドで使用可能な未使用スタックの量が含まれます。このあとに可能であればスタック・フレームが続き、最大100件のフレームが出力されます。C/C++フレームの場合、ライブラリ名も出力される可能性があります。注意: 致命的エラーの状態によっては、スタックが破損している可能性があり、この詳細が利用できない場合があります。

Stack: [0x00007f10345c0000,0x00007f10346c0000],  sp=0x00007f10346be930,  free space=1018k
Native frames: (J=compiled Java code, A=aot compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libMyApp.so+0x57d]  Java_MyApp_readData+0x11
j  MyApp.readData()I+0
j  MyApp.main([Ljava/lang/String;)V+15
v  ~StubRoutines::call_stub
V  [libjvm.so+0x839eea]  JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, Thread*)+0x47a
V  [libjvm.so+0x896fcf]  jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*) [clone .isra.90]+0x21f
V  [libjvm.so+0x8a7f1e]  jni_CallStaticVoidMethod+0x14e
C  [libjli.so+0x4142]  JavaMain+0x812
C  [libpthread.so.0+0x7e9a]  start_thread+0xda

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  MyApp.readData()I+0
j  MyApp.main([Ljava/lang/String;)V+15
v  ~StubRoutines::call_stub

ログには2つのスレッド・スタックが含まれます。

- 1つ目のスレッド・スタックはNative framesであり、すべての関数呼出しを示すネイティブ・スレッドを出力します。ただし、このスレッド・スタックはランタイム・コンパイラでインライン化されるJavaメソッドを考慮に入れません。そのため、インライン化されたメソッドは、親のスタック・フレームの一部として表示されます。
ネイティブ・フレームのスレッド・スタックの情報は、クラッシュの原因に関する重要な情報を提供します。リスト内のライブラリを上から順に分析すれば、通常は、問題の原因となったライブラリを特定し、そのライブラリを担当する適切な組織に問題を報告することができます。

- 2番目のスレッド・スタックであるJavaフレームでは、Javaフレーム(インライン化されたメソッドを含む)が出力され、ネイティブ・フレームはスキップされます。クラッシュによっては、ネイティブ・スレッド・スタックを出力できなくても、Javaフレームを出力できる可能性があります。

追加詳細

エラーがVMスレッドまたはコンパイラ・スレッドで発生した場合は、次の例の追加の詳細が表示されることがあります。たとえばVMスレッドの場合、致命的エラーの発生時にVMスレッドがVM操作を実行していた場合、そのVM操作が出力されます。次の出力例の場合、致命的エラーを起こしたのはコンパイラ・スレッドです。タスクはコンパイラ・タスクであり、HotSpot Client VMがメソッドhs101t004Thread.ackermannをコンパイルしています。

Current CompileTask:
HotSpot Client Compiler:754   b
nsk.jvmti.scenarios.hotswap.HS101.hs101t004Thread.ackermann(IJ)J (42 bytes)

HotSpot Server VMの場合はコンパイラ・タスクの出力が若干異なりますが、やはり完全なクラス名とメソッドを含んでいます。

プロセス・セクションの形式

プロセス・セクションはスレッド・セクションのあとに出力されます。

ここには、プロセスのスレッド・リストやメモリー使用状況など、プロセス全体に関する情報が含まれます。

スレッド・リスト

スレッド・リストには、次の例に示すように、VMが認識しているスレッドが含まれます。

=>0x0805ac88 JavaThread "main" [_thread_in_native, id=21139, stack(0x00007f10345c0000,0x00007f10346c0000)]
|      |         |        |             |             |                                        + stack
|      |         |        |             |             +---------------------------------------- ID
|      |         |        |             +------------------------------------------------------ state
|      |         |        |                                                             (JavaThread only)
|      |         |        +-------------------------------------------------------------------- name
|      |         +----------------------------------------------------------------------------- type
|      +--------------------------------------------------------------------------------------- pointer
+---------------------------------------------------------------------------------------------- "=>" current thread

これには、すべてのJavaスレッドおよび一部のVM内部スレッドが含まれますが、次の例に示すように、VMに接続していないユーザー・アプリケーションによって作成されたネイティブ・スレッドは含まれていません。

Java Threads: ( => current thread )
0x00007f102c469800 JavaThread "C2 CompilerThread0" daemon [_thread_blocked, id=18302, stack(0x00007f0f16f31000,0x00007f0f17032000)]
0x00007f102c468000 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=18301, stack(0x00007f0f17032000,0x00007f0f17133000)]
0x00007f102c450800 JavaThread "Finalizer" daemon [_thread_blocked, id=18298, stack(0x00007f0f173fc000,0x00007f0f174fd000)]
0x00007f102c448800 JavaThread "Reference Handler" daemon [_thread_blocked, id=18297, stack(0x00007f0f174fd000,0x00007f0f175fe000)]
=>0x00007f102c013000 JavaThread "main" [_thread_in_native, id=18245, stack(0x00007f10345c0000,0x00007f10346c0000)]

Other Threads:
0x00007f102c43f000 VMThread "VM Thread" [stack: 0x00007f0f175ff000,0x00007f0f176ff000] [id=18296]
0x00007f102c54b000 WatcherThread [stack: 0x00007f0f15bfb000,0x00007f0f15cfb000] [id=18338]

スレッド・タイプやスレッド状態については、「スレッド・セクションの形式」で説明されています。

VMの状態

その次の情報はVMの状態で、これは仮想マシン全体の状態を示します。表A-3は、一般的な状態を説明しています。

表A-3 VMの状態

一般的なVMの状態

説明

not at a safepoint

正常実行。

at safepoint

特殊なVM操作が完了するまで、VM内のすべてのスレッドがブロックされています。

synchronizing

特殊なVM操作が必要となったため、VM内のすべてのスレッドがブロックされるまでVMが待機しています。

VM状態の出力は次のように、エラー・ログ内で1行になります。

VM state:not at safepoint (normal execution)

相互排他ロックとモニター

エラー・ログ内のその次の情報は、次の例に示すように、現在スレッドによって所有されている相互排他ロックとモニターのリストです。これらの相互排他ロックは、Javaオブジェクトに関連付けられたモニターではなく、VM内部のロックです。VMロックが保持された状態でクラッシュが発生した場合の出力を示す例を次に示します。ログにはロックごとに、ロックの名前、所有者、VM内部の相互排他ロック構造体のアドレス、およびそのOSロックのアドレスが含まれます。この情報は一般に、HotSpot VMにきわめて習熟したユーザーにしか役に立ちません。所有者スレッドはスレッド・リストと相互参照できます。

VM Mutex/Monitor currently owned by a thread:
([mutex/lock_event])[0x007357b0/0x0000031c] Threads_lock - owner thread: 0x00996318
[0x00735978/0x000002e0] Heap_lock - owner thread: 0x00736218

ヒープ・サマリー

次の例に示すように、その次の情報は、ヒープ・サマリーです。出力はガベージ・コレクション(GC)の構成に依存します。この例ではシリアル・コレクタが使用され、クラス・データ共有が無効にされ、Tenured世代が空になっています。これはおそらく、初期段階や起動中に致命的なエラーが発生し、GCがまだどのオブジェクトもTenured世代に昇格させていなかったことを示しています。

Heap
def new generation   total 576K, used 161K [0x46570000, 0x46610000, 0x46a50000)
eden space 512K,  31% used [0x46570000, 0x46598768, 0x465f0000)
from space 64K,   0% used [0x465f0000, 0x465f0000, 0x46600000)
to   space 64K,   0% used [0x46600000, 0x46600000, 0x46610000)
tenured generation   total 1408K, used 0K [0x46a50000, 0x46bb0000, 0x4a570000)
the space 1408K,   0% used [0x46a50000, 0x46a50000, 0x46a50200, 0x46bb0000)
compacting perm gen  total 8192K, used 1319K [0x4a570000, 0x4ad70000, 0x4e570000)
the space 8192K,  16% used [0x4a570000, 0x4a6b9d48, 0x4a6b9e00, 0x4ad70000)
No shared spaces configured.

メモリー・マップ

ログ内のその次の情報は、クラッシュ時の仮想メモリー領域のリストです。アプリケーションが大きいと、このリストが長くなる可能性があります。メモリー・マップは一部のクラッシュをデバッグする際に非常に役立つ可能性がありますが、それは、実際に使用されていたライブラリやそのメモリー内での場所はもちろん、ヒープ、スタック、およびガード・ページの場所もわかるからです。

メモリー・マップの形式はオペレーティング・システム固有です。Oracle Solarisオペレーティング・システムではベース・アドレスとライブラリ名が出力されます。Linuxシステムではプロセス・メモリー・マップ(/proc/pid/maps)が出力されます。Windowsシステムでは各ライブラリのベース・アドレスと終了アドレスが出力されます。次の例は、Linux/x86で生成された出力を示しています。

注意:
この例では簡潔にするために、ほとんどの行を省略しました。

Dynamic libraries:
00400000-00401000 r-xp 00000000 00:47 1374716350                         /export/java_re/jdk/9/ea/167/binaries/linux-x64/bin/java
00601000-00602000 rw-p 00001000 00:47 1374716350                         /export/java_re/jdk/9/ea/167/binaries/linux-x64/bin/java
016c6000-016e7000 rw-p 00000000 00:00 0                                  [heap]
82000000-102000000 rw-p 00000000 00:00 0
102000000-800000000 ---p 00000000 00:00 0
40014000-40015000 r--p 00000000 00:00 0
Lines omitted.
7f0f159f8000-7f0f159f9000 r-xp 00000000 08:11 116808980                  /export/users/dh198349/tests/hs-err/libMyApp.so
7f0f159f9000-7f0f15bf8000 ---p 00001000 08:11 116808980                  /export/users/dh198349/tests/hs-err/libMyApp.so
7f0f15bf8000-7f0f15bf9000 r--p 00000000 08:11 116808980                  /export/users/dh198349/tests/hs-err/libMyApp.so
7f0f15bf9000-7f0f15bfa000 rw-p 00001000 08:11 116808980                  /export/users/dh198349/tests/hs-err/libMyApp.so
Lines omitted.
7f0f15dfc000-7f0f15e00000 ---p 00000000 00:00 0
7f0f15e00000-7f0f15efd000 rw-p 00000000 00:00 0
7f0f15efd000-7f0f15f13000 r-xp 00000000 00:47 1374714565                 /export/java_re/jdk/9/ea/167/binaries/linux-x64/lib/libnet.so
7f0f15f13000-7f0f16113000 ---p 00016000 00:47 1374714565                 /export/java_re/jdk/9/ea/167/binaries/linux-x64/lib/libnet.so
7f0f16113000-7f0f16114000 rw-p 00016000 00:47 1374714565                 /export/java_re/jdk/9/ea/167/binaries/linux-x64/lib/libnet.so
7f0f16114000-7f0f16124000 r-xp 00000000 00:47 1374714619                 /export/java_re/jdk/9/ea/167/binaries/linux-x64/lib/libnio.so
Lines omitted.
7f0f17032000-7f0f17036000 ---p 00000000 00:00 0
7f0f17036000-7f0f17133000 rw-p 00000000 00:00 0
7f0f17133000-7f0f173fc000 r--p 00000000 08:02 2102853                    /usr/lib/locale/locale-archive
7f0f173fc000-7f0f17400000 ---p 00000000 00:00 0
Lines omtted.

次に、エラー・ログ内のメモリー・マップの形式を示します。

40049000-4035c000 r-xp 00000000 03:05 824473 /jdk1.5/jre/lib/i386/client/libjvm.so
|<------------->|  ^      ^       ^     ^    |<--------------------------------->|
Memory region    |      |       |     |                      |
|      |       |     |                      |
Permission   --- +      |       |     |                      |
r: read               |       |     |                      |
w: write              |       |     |                      |
x: execute            |       |     |                      |
p: private            |       |     |                      |
s: share              |       |     |                      |
|       |     |                      |
File offset   ----------+       |     |                      |
|     |                      |
Major ID and minor ID of -------+     |                      |
the device where the file             |                      |
is located (i.e. /dev/hda5)           |                      |
|                      |
inode number  ------------------------+                      |
|
File name  --------------------------------------------------+

この例に示されたメモリー・マップの出力では、各ライブラリに2つの仮想メモリー領域(コード用とデータ用に1つずつ)があります。コード・セグメントのアクセス権はr-xp (読取り可能、実行可能、非公開)でマークされ、データ・セグメントのアクセス権はrw-p (読取り可能、書込み可能、非公開)になります。

Javaヒープはすでに出力の前のほうのヒープ・サマリーに含まれていますが、ヒープ用に予約された実際のメモリー領域がヒープ・サマリーの値と一致することや、属性がrwxpに設定されていることを確認することは、有用である可能性があります。

スレッド・スタックは通常、メモリー・マップ内で連続する2つの領域、具体的にはアクセス権が---pの領域(ガード・ページ)とアクセス権がrwxpの領域(実際のスタック領域)として表示されます。さらに、ガード・ページ・サイズやスタック・サイズを知ることは有用です。たとえばこのメモリー・マップでは、スタックの場所は4127b000から412fb000です。

Windowsシステムでは次の例のように、メモリー・マップの出力は、ロードされた各モジュールのロード・アドレスと終了アドレスになります。

Dynamic libraries:
0x00400000 - 0x0040c000     c:\jdk6\bin\java.exe
0x77f50000 - 0x77ff7000     C:\WINDOWS\System32\ntdll.dll
0x77e60000 - 0x77f46000     C:\WINDOWS\system32\kernel32.dll
0x77dd0000 - 0x77e5d000     C:\WINDOWS\system32\ADVAPI32.dll
0x78000000 - 0x78087000     C:\WINDOWS\system32\RPCRT4.dll
0x77c10000 - 0x77c63000     C:\WINDOWS\system32\MSVCRT.dll
0x08000000 - 0x08183000     c:\jdk6\jre\bin\client\jvm.dll
0x77d40000 - 0x77dcc000     C:\WINDOWS\system32\USER32.dll
0x7e090000 - 0x7e0d1000     C:\WINDOWS\system32\GDI32.dll
0x76b40000 - 0x76b6c000     C:\WINDOWS\System32\WINMM.dll
0x6d2f0000 - 0x6d2f8000     c:\jdk6\jre\bin\hpi.dll
0x76bf0000 - 0x76bfb000     C:\WINDOWS\System32\PSAPI.DLL
0x6d680000 - 0x6d68c000     c:\jdk6\jre\bin\verify.dll
0x6d370000 - 0x6d38d000     c:\jdk6\jre\bin\java.dll
0x6d6a0000 - 0x6d6af000     c:\jdk6\jre\bin\zip.dll
0x10000000 - 0x10032000     C:\bugs\crash2\App.dll

VM引数と環境変数

エラー・ログ内のその次の情報は、次の例に示すように、VM引数のリストと、それに続く環境変数のリストです。

VM Arguments:
jvm_args:
java_command: MyApp
java_class_path (initial): .
Launcher Type: SUN_STANDARD

Logging:
Log output configuration:
#0: stdout all=warning uptime,level,tags
#1: stderr all=off uptime,level,tags

Environment Variables:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
DISPLAY=localhost:10.0
ARCH=i386

注意:

この環境変数のリストは、Java VMに適用可能な環境変数の完全なリストではなく、その一部です。

シグナル・ハンドラ

Oracle SolarisおよびLinuxオペレーティング・システムでは、次の例に示すように、エラー・ログ内のその次の情報はシグナル・ハンドラのリストです。

Signal Handlers:
SIGSEGV: [libjvm.so+0xd48840], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO
SIGBUS: [libjvm.so+0xd48840], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO
SIGFPE: [libjvm.so+0xd48840], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO
SIGPIPE: [libjvm.so+0xb60080], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO
SIGXFSZ: [libjvm.so+0xb60080], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO
SIGILL: [libjvm.so+0xd48840], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO
SIGUSR2: [libjvm.so+0xb5ff40], sa_mask[0]=00000000000000000000000000000000, sa_flags=SA_RESTART|SA_SIGINFO
SIGHUP: [libjvm.so+0xb60150], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO
SIGINT: [libjvm.so+0xb60150], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO
SIGTERM: [libjvm.so+0xb60150], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO
SIGQUIT: [libjvm.so+0xb60150], sa_mask[0]=11111111011111111101111111111110, sa_flags=SA_RESTART|SA_SIGINFO

システム・セクションの形式

エラー・ログの最後のセクションは、システム情報です。その出力はオペレーティング・システムに固有ですが、一般にオペレーティング・システムのバージョン、CPU情報、およびメモリー構成に関するサマリー情報を含みます。

次の例では、Linuxオペレーティング・システムでの出力を示します。

---------------  S Y S T E M  ---------------

OS:DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=12.04
DISTRIB_CODENAME=precise
DISTRIB_DESCRIPTION="Ubuntu 12.04 LTS"
uname:Linux 3.2.0-24-generic #39-Ubuntu SMP Mon May 21 16:52:17 UTC 2012 x86_64
libc:glibc 2.15 NPTL 2.15
rlimit: STACK 8192k, CORE infinity, NPROC 1160369, NOFILE 4096, AS infinity
load average:0.46 0.33 0.27

/proc/meminfo:
MemTotal:       148545440 kB
MemFree:         1020964 kB
Buffers:        29600728 kB
Cached:         86607768 kB
SwapCached:        16112 kB
Active:         52272944 kB
Inactive:       64862992 kB
Active(anon):     314080 kB
Inactive(anon):   616296 kB
Active(file):   51958864 kB
Inactive(file): 64246696 kB
Unevictable:          16 kB
Mlocked:              16 kB
SwapTotal:       1051644 kB
SwapFree:         976092 kB
Dirty:                40 kB
Writeback:             0 kB
AnonPages:        912404 kB
Mapped:            95804 kB
Shmem:              2936 kB
Slab:           28625980 kB
SReclaimable:   28337400 kB
SUnreclaim:       288580 kB
KernelStack:        6040 kB
PageTables:        42524 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:    75324364 kB
Committed_AS:    6172612 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      681668 kB
VmallocChunk:   34282379392 kB
HardwareCorrupted:     0 kB
AnonHugePages:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:      171520 kB
DirectMap2M:     8208384 kB
DirectMap1G:    142606336 kB

CPU:total 24 (initial active 24) (6 cores per cpu, 2 threads per core) family 6 model 44 stepping 2, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt, aes, clmul, ht, tsc, tscinvbit, tscinv
CPU Model and flags from /proc/cpuinfo:
model name    : Intel(R) Xeon(R) CPU           X5675  @ 3.07GHz
flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 sse4_2 popcnt aes lahf_lm ida arat epb dts tpr_shadow vnmi flexpriority ept vpid

Memory: 4k page, physical 148545440k(1020964k free), swap 1051644k(976092k free)

vm_info: Java HotSpot(TM) 64-Bit Server VM (9-ea+167) for linux-amd64 JRE (9-ea+167), built on Apr 27 2017 00:28:45 by "javare" with gcc 4.9.2

Oracle SolarisおよびLinuxの場合、オペレーティング・システム情報はファイル/etc/*releaseにあります。このファイルは、アプリケーションが実行されているシステムの種類について説明し、場合によっては情報文字列にパッチ・レベルが含まれることもあります。/etc/*releaseファイルには一部のシステム・アップグレードが反映されません。ユーザーがシステムの任意の部分を構築し直せるLinuxシステムの場合は、特にそうなります。

Oracle Solarisオペレーティング・システムでは、unameシステム・コールを使用してカーネルの名前を取得します。スレッド・ライブラリ(T1またはT2)も出力されます。

Linuxシステムの場合もunameシステム・コールを使用してカーネル名が取得されます。次の例に示すように、libcのバージョンとスレッド・ライブラリのタイプも出力されます。

uname:Linux 3.2.0-24-generic #39-Ubuntu SMP Mon May 21 16:52:17 UTC 2012 x86_64
libc:glibc 2.15           NPTL 2.15
|<- glibc version ->|<--  pthread type       -->|

Linuxの場合、可能なスレッド・タイプは3つあります(具体的にはlinuxthreads (fixed stack)、linuxthreads (floating stack)、およびNPTL)。これらは通常、/lib、/lib/i686、および/lib/tlsにインストールされます。

スレッド・タイプを知ることは有用です。たとえば、クラッシュがpthreadに関係しているように見える場合、別のpthreadライブラリを選択すれば問題を回避できる可能性があります。別のpthreadライブラリ(およびlibc)を選択するには、LD_LIBRARY_PATHまたはLD_ASSUME_KERNELを設定します。

glibcのバージョンには通常、パッチ・レベルは含まれません。コマンドrpm -q glibcを実行すると、より詳しいバージョン情報が得られる可能性があります。

Oracle SolarisおよびLinuxオペレーティング・システムでは、その次の情報はrlimit情報です。

注意:
次の例に示すように、VMのデフォルトのスタック・サイズは通常、システムの制限より小さくなっています。

rlimit: STACK 8192k, CORE infinity, NPROC 1160369, NOFILE 4096, AS infinity
|          |                  |           |           virtual memory (-v)
|          |                  |           +--- max open files (ulimit -n)
|          |                  +----------- max user processes (ulimit -u)
|          +------------------------------ core dump size (ulimit -c)
+----------------------------------------- stack size (ulimit -s)
load average:0.04 0.05 0.02

rlimit: STACK 8192k, CORE 0k, NPROC 4092, NOFILE 1024, AS infinity
|          |         |           |           virtual memory (-v)
|          |         |           +--- max open files (ulimit -n)
|          |         +----------- max user processes (ulimit -u)
|          +------------------------- core dump size (ulimit -c)
+---------------------------------------- stack size (ulimit -s)
load average:0.04 0.05 0.02

その次の情報は、次の例に示すように、起動時にVMによって識別されたCPUのアーキテクチャと機能を指定します。

CPU:total 24 (initial active 24) (6 cores per cpu, 2 threads per core) family 6 model 44 stepping 2, cmov, cx8, fxsr, mmx
|                                                                   |                      | |<----- CPU features ---->|
|                                                                   |
|                                                                   +--- processor family (IA32 only):
|                                                                   3 - i386
|                                                                   4 - i486
|                                                                   5 - Pentium
|                                                                   6 - PentiumPro, PII, PIII
|                                                                   15 - Pentium 4
+------------ Total number of CPUs

表A-4は、SPARCシステムで可能なCPU機能を示しています。

表A-4 SPARCの機能

SPARCの機能

説明

has_v8

v8命令をサポートします。

has_v9

v9命令をサポートします。

has_vis1

視覚化命令をサポートします。

has_vis2

視覚化命令をサポートします。

is_ultra3

UltraSparc III。

no-muldiv

ハードウェア整数乗除なし。

no-fsmuld

Multiply-AddおよびMultiply-Subtract命令なし。

表A-5は、Intel/IA32システムで可能なCPU機能を示しています。

表A-5 Intel/IA32の機能

Intel/IA32の機能

説明

cmov

cmov命令をサポートします。

cx8

cmpxchg8b命令をサポートします。

fxsr

fxsaveとfxrstorをサポートします。

mmx

MMXをサポートします。

sse

SSE拡張をサポートします。

sse2

SSE2拡張をサポートします。

ht

ハイパースレッディング・テクノロジをサポートします。

表A-6は、AMD64/EM64Tシステムで可能なCPU機能を示しています。

表A-6 AMD64/EM64Tの機能

AMD64/EM64Tの機能

説明

amd64

AMD Opteron、Athlon64など。

em64t

Intel EM64Tプロセッサ。

3dnow

3DNow拡張をサポートします。

ht

ハイパースレッディング・テクノロジをサポートします。

エラー・ログ内のその次の情報は、次の例に示すように、メモリー情報です。

unused swap space
total amount of swap space        |
unused physical memory            |          |
total amount of physical memory       |              |          |
page size              |         |              |          |
v                v         v              v          v
Memory: 4k page, physical 513604k(11228k free), swap 530104k(497504k free)

一部のシステムではスワップ空間が実際の物理メモリーの少なくとも2倍であることを要求しますが、要件を持たないシステムもあります。一般に、物理メモリーとスワップ空間の両方がほぼいっぱいになっていれば、クラッシュの原因としてメモリー不足を疑ってよいでしょう。

Linuxシステムではカーネルが、未使用の物理メモリーの大部分をファイル・キャッシュに変換する場合があります。追加のメモリーが必要になると、Linuxカーネルはキャッシュ・メモリーをアプリケーションに戻します。この処理はカーネルによって透過的に行われますが、それは、使用可能な物理メモリーがまだ十分存在しても、致命的エラー・ハンドラが報告する未使用物理メモリーの量がゼロに近くなる可能性があることを意味します。

エラー・ログのSYSTEMセクション内の最後の情報はvm_infoで、これはlibjvm.so/jvm.dllに埋め込まれたバージョン文字列です。どのJava VMも一意のvm_info文字列を持ちます。特定のJava VMによって致命的エラー・ログが生成されたのかどうかを疑問に思う場合には、バージョン文字列をチェックしてください。

B Java 2Dのプロパティ

この付録では、Java 2Dのトラブルシューティングに役立つ可能性のあるプロパティを示します。

この付録の内容は次のとおりです。

- Oracle SolarisおよびLinuxでのプロパティ

- Windows上のプロパティ

Oracle SolarisおよびLinuxでのプロパティ

Oracle SolarisおよびLinuxのJava 2Dプロパティのリスト。

表B-1は、Oracle SolarisおよびLinuxプラットフォーム上のいくつかの有用なプロパティのデフォルト値の説明です。

表B-1 Oracle SolarisおよびLinuxでのデフォルトのJava 2Dプロパティ

設定

DGA

SHM

ピックスマップ

オンスクリーン

オフスクリーン

DGA対応のOracle Solaris SPARC

オン

オン

オフ

DGA/ソフトウェア

ソフトウェア

DGA非対応のOracle Solaris SPARC、Oracle Solaris x86、Linux、SunRay、VNC

オフ

オン

オン

X11/MITSHM

共有/サーバー・ピックスマップ

J2SE 1.4以上: リモートXサーバー、ssh

オフ

オフ

オン

X11

サーバー・ピックスマップ

J2SE 1.3.1以下: リモートXサーバー、ssh

オフ

オフ

オフ

X11

ソフトウェア

次のリストでは、デフォルトの変更方法について説明します。

- Oracle SolarisおよびLinuxのデフォルト・パイプラインはX11パイプラインです。このデフォルトを変更するには次のようにします。

- -Dsun.java2d.opengl=true — OpenGLパイプラインの有効化を試みます。

- DGAの使用を制御するには、次のようにします。

- NO_J2D_DGA未設定 - 使用可能であればDGAを使用します。

- NO_J2D_DGA設定 - DGAの使用を無効にします。

- MIT共有メモリー拡張(SHM)を制御するには、次のようにします。

- 使用可能な場合にSHMを使用するには、次のいずれかのプロパティを指定します。
NO_J2D_MITSHM未設定

J2D_USE_MITSHM=true

- SHMを使用しない場合は、次のいずれかのプロパティを指定します。
NO_J2D_MITSHM set

J2D_USE_MITSHM=false

- ピックスマップの一般的な使用を制御するには、次のようにします。

- -Dsun.java2d.pmoffscreen未設定 - DGAを使用できない場合にピックスマップを使用します。

- -Dsun.java2d.pmoffscreen=true - ピックスマップの使用を強制します。

- -Dsun.java2d.pmoffscreen=false - ピックスマップの使用を無効にします。

- 共有/サーバー・ピックスマップの使用を制御するには、次のようにします。

- J2D_PIXMAPS未設定 - 両方のタイプを使用します。

- J2D_PIXMAPS=shared - 共有メモリー・ピックスマップのみを使用します。

- J2D_PIXMAPS=sserver - サーバー側ピックスマップのみを使用します。

- デフォルト・ビジュアルの選択を制御するには、次のようにします。

- FORCEDEFVIS未設定(デフォルト) - 使用可能な最適なビジュアルを使用します。

- FORCEDEFVISを16進値に設定 - その16進値をIDに持つビジュアルを使用します。

- FORCEDEFVISを他の値に設定 - デフォルト・ビジュアルを使用します。

Windows上のプロパティ

Windows上の有用なプロパティのリスト。

次のリストでは、Windowsプラットフォーム上のいくつかの有用なプロパティについて説明します。

- DirectDraw/GDIパイプラインがWindowsのデフォルトのパイプラインです。このデフォルトを変更するには次のようにします。

- -Dsun.java2d.noddraw=true - DirectDrawパイプラインの使用を無効にします。代わりにGDIが使用されます。

- -Dsun.java2d.noddraw=false - DirectDrawパイプラインの使用を有効にします。

- -Dsun.java2d.d3d=false - Direct3Dパイプラインの使用を無効にします。

- J2D_D3D=false - Direct3Dパイプラインの使用を無効にします。

- -Dsun.java2d.d3d=true - Direct3Dパイプラインの使用を有効にします。

- J2D_D3D=true - Direct3Dパイプラインの使用を有効にします。

- 組み込み表面パント・メカニズムの使用を制御するには、次のようにします。

- -Dsun.java2d.ddforcedram=true - 揮発性イメージをVRAMに保持します。

- DirectDraw Blit操作の使用を制御するには、次のようにします。

- -Dsun.java2d.ddblit=false - DirectDraw Blit操作の使用を無効にします。代わりにGDI Blitが使用されます。

C 環境変数とシステム・プロパティ

この付録では、Java HotSpot VMの問題をトラブルシューティングする際に役立つ可能性のある環境変数とシステム・プロパティについて説明します。

「バグ・レポートの提出」の「環境変数」に、環境変数の収集に関する情報が含まれています。

この付録の内容は次のとおりです。

- JAVA_HOME環境変数

- JAVA_TOOL_OPTIONS環境変数

- java.security.debugシステム・プロパティ

JAVA_HOME環境変数

この変数は、Java Development Kit (JDK)ソフトウェアがインストールされているディレクトリを示します。

JAVA_TOOL_OPTIONS環境変数

多くの環境では、必要なコマンド行オプションを使用してアプリケーションを起動するコマンド行には容易にアクセスできません。

これは、埋込みVMを使用するアプリケーション(VMの起動にJava Native Interface (JNI)呼出しAPIを使用することを意味する)や、起動がスクリプト内で深く入れ子になっている場合に頻繁に起こります。このような環境では、JAVA_TOOL_OPTIONS環境変数がコマンド行の拡張に役立つ可能性があります。

この環境変数が設定されていると、JNI_CreateJavaVM関数(JNI呼び出しAPIの中にある)はそのJavaVMInitArgs引数で提供されるオプションの先頭に、その環境変数の値を追加します。

注意:

セキュリティ上の理由により、このオプションが無効になる場合があります。たとえば、Oracle Solarisオペレーティング・システムでは、有効なユーザーまたはグループのIDが実際のIDと異なる場合に、このオプションが無効になります。

この環境変数では、ツールの初期化(具体的には-agentlibまたは-javaagentオプションを使用したネイティブまたはJavaプログラミング言語エージェントの起動)を指定できます。

この変数は、診断の目的でコマンド行をほかのオプションを使って拡張するためにも使用できます。たとえば、-XX:OnErrorオプションを使用すると、致命的エラーが発生した場合に実行されるスクリプトまたはコマンドを指定できます。

この環境変数はJNI_CreateJavaVM関数の呼出し時に検査されるため、通常はランチャによって処理されるオプションを使用したコマンド行の拡張(-clientまたは-serverオプションを使ったVM選択など)には使用できません。

java.security.debugシステム・プロパティ

このシステム・プロパティは、Java Runtime Environment (JRE)のセキュリティ・システムが、実行中にトレース・メッセージを出力するかどうかを制御します。

このオプションは、SecurityExceptionがスローされたときにセキュリティ・マネージャに関連する問題を診断する場合に役立つ可能性があります。

java.security.debugプロパティには、次の値を指定できます。

- access
checkPermissionの結果をすべて出力します。

次の追加のオプションをaccessオプションとともに指定できます。

- stack
スタック・トレースを含めます。

- domain
コンテキスト内のすべてのドメインをダンプします。

- failure
例外をスローする前に、アクセス権を保持しなかったスタックおよびドメインをダンプします。

- jar
JARの検証情報を出力します。

- policy
SecureClassLoaderによって割り当てられるアクセス権を出力します。

- scl
たとえば、checkPermissionのすべての結果を出力し、コンテキスト内のすべてのドメインをトレースするには、java.security.debugプロパティをaccess,stackに設定します。アクセス障害をトレースするには、このプロパティをaccess,failureに設定します。

次の例は、checkPermission障害の出力を示しています。

$ java -Djava.security.debug="access,failure" MyApp
access denied (java.net.SocketPermission server.foobar.com resolve
)
java.lang.Exception: Stack trace
at java.lang.Thread.dumpStack(Thread.java:1158)
at java.security.AccessControlContext.checkPermission
(AccessControlContext.java:253)
at java.security.AccessController.checkPermission(AccessController.java:427)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.SecurityManager.checkConnect(SecurityManager.java:1031)
at java.net.InetAddress.getAllByName0(InetAddress.java:1117)
at java.net.InetAddress.getAllByName0(InetAddress.java:1098)
at java.net.InetAddress.getAllByName(InetAddress.java:1061)
at java.net.InetAddress.getByName(InetAddress.java:958)
at java.net.InetSocketAddress.<init>(InetSocketAddress.java:124)
at java.net.Socket.<init>(Socket.java:178)
at MyApp.main(MyApp.java:7)

java.security.debugシステム・プロパティの詳細は、Java Platform, Standard Editionセキュリティ開発者ガイドのセキュリティのトラブルシューティングに関する項を参照してください。

D コマンド行オプション

この付録では、Java HotSpot VMの問題を診断する際に役立つ可能性のあるいくつかのコマンド行オプションについて説明します。

この付録の内容は次のとおりです。

- Java HotSpot VMコマンド行オプション

- その他のコマンド行オプション

Java HotSpot VMコマンド行オプション

「-XX」という接頭辞が付いたコマンド行オプションは、Java HotSpot仮想マシンに固有のものです。これらのオプションの多くは、パフォーマンス・チューニングや診断を行うために重要であるため、この付録で説明されています。

使用可能なすべての-XXオプションの詳細は、「Java HotSpot VMオプション」を参照してください。

jinfo -flagコマンドを使用すると、指定されたJavaプロセスの特定のJava VMフラグの値を動的に設定、設定解除または変更できます。「jinfoユーティリティ」およびJConsoleユーティリティを参照してください。

これらのフラグの完全なリストについては、JConsoleユーティリティの「MBean」タブを使用してください。com.sun.managementドメインにあるHotSpotDiagnostic MBeanのDiagnosticOptions属性の値のリストを参照してください。フラグは次のとおりです。

- HeapDumpOnOutOfMemoryError

- HeapDumpPath

- PrintGC

- PrintGCDetails

- PrintGCTimeStamps

- PrintClassHistogram

- PrintConcurrentLocks

-XX:HeapDumpOnOutOfMemoryErrorオプション

このオプションは、JavaヒープまたはPermanent世代からの割当てが満たされない場合に、Java HotSpot VMにヒープ・ダンプを生成するよう指示します。このオプションを使用して実行するときのオーバーヘッドはないため、OutOfMemoryError例外が表示されるまでに長い時間のかかる本番システムに役立つ可能性があります。

JConsoleユーティリティの「MBean」タブを使用して、このオプションを実行時に指定することもできます。

次の例は、このフラグ・セットによるメモリー不足という結果を示しています。

$ java -XX:+HeapDumpOnOutOfMemoryError -mn256m -mx512m ConsumeHeap
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid2262.hprof ...
Heap dump file created [531535128 bytes in 14.691 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at ConsumeHeap$BigObject.(ConsumeHeap.java:22)
at ConsumeHeap.main(ConsumeHeap.java:32)

ConsumeHeapによってJavaヒープがいっぱいになり、メモリーが使い果たされます。java.lang.OutOfMemoryError例外がスローされると、ヒープ・ダンプ・ファイルが作成されます。この場合、そのファイルは507MBで、現在のディレクトリにjava_pid2262.hprofという名前で作成されます。

デフォルトでは、前述の例のように、ヒープ・ダンプはVMの作業ディレクトリにあるjava_pidpid.hprofと呼ばれるファイル内に作成されます。-XX:HeapDumpPath=オプションを使用すれば、別のファイル名またはディレクトリを指定できます。たとえば、-XX:HeapDumpPath=/disk2/dumpsを指定すると、ヒープ・ダンプは/disk2/dumpsディレクトリに生成されます。

-XX:OnErrorオプション

致命的エラーが発生すると、Java HotSpot VMはオプションでユーザー指定のスクリプトまたはコマンドを実行できます。スクリプトまたはコマンドは、-XX:OnError=stringコマンド行オプションを使用して指定されます(stringは単一のコマンド、またはセミコロンで区切られたコマンドのリスト)。この文字列内では、%pのすべての出現箇所が現在のPIDに置き換えられ、%%のすべての出現箇所が単一の%に置き換えられます。次の例は、javaランチャを使用してMyAppという名前のJavaアプリケーションを起動するときに、このオプションをどのように使用できるかを示しています。

- java -XX:OnError="pmap %p" MyApp
Oracle Solarisオペレーティング・システムでは、pmapコマンドはプロセスのアドレス空間に関する情報を表示します。この例では、致命的エラーが発生した場合にpmapコマンドが実行されて、現在のプロセスのアドレス空間が表示されます。

- java -XX:OnError="cat hs_err_pid%p.log | mail support@acme.com" MyApp
前述の例では、致命的エラーが発生した場合に、致命的エラー・ログ・ファイルの内容がサポートの別名にメール送信されます。

- java -XX:OnError="gcore %p; dbx - %p" MyApp
Oracle Solarisオペレーティング・システムでは、gcoreコマンドは指定されたプロセスのコア・イメージを作成し、dbxコマンドはデバッガを起動します。前述の例では、予想外のエラーが発生した場合に、gcoreコマンドが実行されて現在のプロセスのコア・イメージが作成され、さらにデバッガが起動されてそのプロセスに接続されます。

- java -XX:OnError="gdb - %p" MyApp
Linuxでは、gdbコマンドはデバッガを起動します。前述の例では、予想外のエラーが発生した場合にgdbデバッガが起動され、現在のプロセスに接続されます。

- java -XX:OnError="userdump.exe %p" MyApp
Windowsでは、userdump.exeユーティリティは指定されたプロセスのクラッシュ・ダンプを作成します。このユーティリティは、Windowsに付属していないため、Microsoft OEM Support Toolsパッケージの一部としてMicrosoftのWebサイトからダウンロードするようにしてください。

この例では、致命的エラーが発生した場合にuserdump.exeユーティリティが実行されて、現在のプロセスのコア・ダンプが作成されます。

注意:

この例では、userdump.exeユーティリティへのパスがPATH変数に定義されているものと想定します。

Windowsでのクラッシュ・ダンプの作成の詳細は、「Windowsでのクラッシュ・ダンプの収集」を参照してください。

-XX:ShowMessageBoxOnErrorオプション

このオプションが設定されているときに致命的エラーが発生した場合、HotSpot VMはその致命的エラーに関する情報を表示し、ネイティブ・デバッガが起動されるかどうかを指定するようユーザーに求めます。Oracle SolarisおよびLinuxオペレーティング・システムの場合は、その出力とプロンプトがアプリケーション・コンソール(標準入力と標準出力)に送信されます。Windowsの場合は、Windowsメッセージ・ボックスが表示されます。

次の例は、Linuxシステムにおける致命的エラーを示しています。

==============================================================================
Unexpected Error
------------------------------------------------------------------------------
SIGSEGV (0xb) at pc=0x2000000001164db1, pid=10791, tid=1026

Do you want to debug the problem?

To debug, run 'gdb /proc/10791/exe 10791'; then switch to thread 1026
Enter 'yes' to launch gdb automatically (PATH must include gdb)
Otherwise, press RETURN to abort...
==============================================================================

この場合は、SIGSEGVエラーが発生し、ユーザーはgdbデバッガが起動されてプロセスに接続するのかどうかを指定するよう求められます。ユーザーが「y」または「yes」と入力すると、gdbが起動されます(それがPATH変数内に設定されているものと想定)。

Oracle Solarisオペレーティング・システムでは、メッセージはLinuxの例と似ていますが、dbxデバッガを起動するようユーザーが求められる点が異なります。

Windowsでは、メッセージ・ボックスが表示されます。ユーザーが「はい」をクリックした場合、VMはデフォルト・デバッガの起動を試みます。このデバッガは、「Windowsでのクラッシュ・ダンプの収集」で説明されているレジストリ設定によって構成されます。Microsoft Visual Studioがインストールされている場合、デフォルト・デバッガは通常msdev.exeになるよう構成されます。

前述の例では、PID (pid=10791)だけでなく、スレッドID (tid=1026)も出力に含まれています。デバッガが起動された場合、デバッガの初期段階の1つでスレッドが選択され、そのスタック・トレースが取得される可能性があります。

プロセスは応答を待っているので、他のツールを使用してクラッシュ・ダンプを取得したり、プロセスの状態を問い合せたりできます。たとえば、Oracle Solarisオペレーティング・システムでは、gcoreユーティリティを使用してコア・ダンプを取得できます。

Windowsでは、userdumpまたはwindbgプログラムを使用して、ワトソン博士のクラッシュ・ダンプを取得できます。windbgユーティリティはMicrosoftのDebugging Tools for Windowsに含まれており、「Windowsでのクラッシュ・ダンプの収集」で説明されています。windbgで、「Attach to a Process」メニュー・オプションを選択すると、プロセスの一覧が表示されてPIDを入力するよう求められます。HotSpot VMでは、PIDを含むメッセージ・ボックスが表示されます。PIDを選択した後、.dump /fコマンドを使用してクラッシュ・ダンプを強制できます。例D-1は、crash.dumpというファイルに作成されたクラッシュ・ダンプの例です。

図D-1 windbgで作成されたクラッシュ・ダンプの例

一般に、デバッガ・ツールを利用できる開発環境では、-XX:+ShowMessageBoxOnErrorオプションの方が役立ちます。致命的エラーの発生時に、決められた一連のコマンドまたはスクリプトが実行される本番環境には、-XX:OnErrorオプションの方が適しています。

その他の-XXオプション

トラブルシューティング時に役立つ可能性のある-XXコマンド行オプションは、他にもいくつかあります。

- -XX:OnOutOfMemoryError=string
このオプションを使用すると、OutOfMemoryError例外がスローされたときに実行されるコマンドまたはスクリプトを指定できます。

- -XX:ErrorFile=filename
このオプションは、致命的エラー・ログ・ファイルの場所を指定するために使用できます。「致命的エラー・ログの場所」を参照してください。

- -xx:HeapDumpPath=path
このオプションは、ヒープ・ダンプの場所を指定するために使用できます。「-XX:HeapDumpOnOutOfMemoryErrorオプション」を参照してください。

- -XX:MaxPermSize=size
このオプションは、Permanent世代メモリーのサイズを指定するために使用できます。「OutOfMemoryError例外の理解」を参照してください。

- -XX:+PrintCommandLineFlags
このオプションはすべてのVMコマンド行フラグを出力するために使用できます。「バグ・レポート用のデータの収集」を参照してください。

- -XX:+PrintConcurrentLocks
このオプションを使用すると、Ctrl+Breakハンドラによって、各スレッドが所有する並行ロックの一覧が出力されます。

- -XX:+PrintClassHistogram
このオプションを使用すると、Ctrl+Breakハンドラによって、ヒープ・ヒストグラムが出力されます。

- -XX:+PrintGCDetails and-XX:+PrintGCTimeStamps
これらのオプションは、ガベージ・コレクションの詳細情報の出力に使用できます。「-verbose:gcオプション」を参照してください。

- -XX:+UseAltSigs
Oracle Solaris 8および9オペレーティング・システムでこのオプションを使用すると、SIGUSR1およびSIGUSR2のかわりに代替シグナルを使用するよう、HotSpot VMに指示できます。「Oracle Solaris、LinuxおよびmacOSでのシグナルの処理」を参照してください。

- -XX:+UseConcMarkSweepGC , -XX:+UseSerialGC and -XX:+UseParallelGC
このオプションは、使用されるガベージ・コレクション・ポリシーを指定するために使用できます。「ガベージ・コレクション中のクラッシュの回避」を参照してください。

その他のコマンド行オプション

-XXオプションに加えて、ほかにも多くのコマンド行オプションがトラブルシューティング情報を提供できます。

このセクションでは、これらのオプションのいくつかについて説明します。

-Xcheck:jniオプション

このオプションは、Java Native Interface (JNI)を使用するアプリケーションの問題を診断する際に役立ちます。ネイティブ・コードにバグが含まれているせいで、HotSpot VMがクラッシュしたり、正しく動作しなかったりする場合があります。

次の例にあるように、-Xcheck:jniオプションは、アプリケーションを起動するコマンド行に追加されます。

java -Xcheck:jni MyApp

-Xcheck:jniオプションを指定すると、VMではJNI関数に渡された引数に対して追加の検証を行います。

注意:
このオプションにより、すべての無効な引数が見つかること、またはアプリケーション・コード内のロジックのバグが診断されることは保証されていませんが、このような問題の多くを診断するのに役立ちます。

無効な引数が検出されると、VMはアプリケーション・コンソールまたは標準出力にメッセージを出力し、問題のスレッドのスタック・トレースを出力してVMを停止します。

次の例では、null値を許可しないJNI関数にnull値が間違って渡されています。

FATAL ERROR in native method: Null object passed to JNI
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:343)
- locked <0x450b9f70> (a java.net.PlainSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:439)
at java.net.ServerSocket.accept(ServerSocket.java:410)
at org.apache.tomcat.service.PoolTcpEndpoint.acceptSocket
(PoolTcpEndpoint.java:286)
at org.apache.tomcat.service.TcpWorkerThread.runIt
(PoolTcpEndpoint.java:402)
at org.apache.tomcat.util.ThreadPool$ControlRunnable.run
(ThreadPool.java:498)
at java.lang.Thread.run(Thread.java:536)

次の例では、jfieldID引数を必要としていたJNI関数に不適切な引数が渡されています。

FATAL ERROR in native method: Instance field not found in JNI get/set
field operations
at java.net.PlainSocketImpl.socketBind(Native Method)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:359)
- locked <0xf082f290> (a java.net.PlainSocketImpl)
at java.net.ServerSocket.bind(ServerSocket.java:318)
at java.net.ServerSocket.<init>(ServerSocket.java:185)
at jvm003a.<init>(jvm003.java:190)
at jvm003a.<init>(jvm003.java:151)
at jvm003.run(jvm003.java:51)
at jvm003.main(jvm003.java:30)

次に、-Xcheck:jniオプションが診断に役立つ可能性のある、その他の問題の例を示します。

- 間違ったスレッド用のJNI環境が使用される場合

- 無効なJNI参照が使用される場合

- 配列以外の型への参照が、配列型を必要とする関数に渡される場合

- 非staticフィールドIDが、staticフィールドIDを必要とする関数に渡される場合

- 例外が保留になっている状態で、JNI呼出しが行われる場合

通常、-Xcheck:jniオプションによって検出されるエラーはすべて致命的エラーです(つまり、エラーが出力されてVMが停止される)。この動作には例外が1つあります。JNI呼出しがJNIクリティカル・リージョン内で行われた場合です。次の例に示すように、この場合は、次の致命的でない警告メッセージが出力されます。

Warning: Calling other JNI functions in the scope of
Get/ReleasePrimitiveArrayCritical or Get/ReleaseStringCritical

JNIクリティカル・リージョンは、ネイティブ・コードがJNI関数GetPrimitiveArrayCriticalまたはGetStringCriticalを使用して、Javaヒープ内の配列または文字列への参照を取得するときに作成されます。この参照は、ネイティブ・コードが対応するrelease関数を呼び出すまで保持されます。getとreleaseの間にあるコードはJNIクリティカル・セクションと呼ばれ、その間HotSpot VMは、ガベージ・コレクションの発生を許可する状態にVMを移行できません。一般的な推奨事項は、JNIクリティカル・セクション内部でその他のJNI関数、特にデッドロックを発生させる可能性のあるJNI関数を使用しないことです。したがって、-Xcheck:jniオプションによって出力された上記の警告は、潜在的な問題を示すものであり、必ずしもアプリケーションのバグを示すとはかぎりません。

-verbose:classオプション

このオプションは、クラスのロードとアンロードのロギングを有効にします。

-verbose:gcオプション

このオプションは、ガベージ・コレクション(GC)情報のロギングを有効にします。これを他のHotSpot VM固有のオプション(-XX:+PrintGCDetailsや-XX:+PrintGCTimeStampsなど)と組み合せることで、GCに関する詳細情報を取得できます。その情報の出力には、各GCの前後の世代のサイズ、ヒープの合計サイズ、昇格されたオブジェクトのサイズ、所要時間などが含まれています。

これらのオプションおよびGCの解析とチューニングの詳細は、GC Portalの記事に記載されています。

-verbose:gcオプションは、実行時に管理APIまたはJVM TIを使用して動的に有効にできます。「カスタム診断ツール」を参照してください。

JConsoleモニタリングおよび管理ツールが管理VMに接続されているときは、そのツールでこのオプションを有効または無効にすることもできます。「JConsole」を参照してください。

-verbose:jniオプション

このオプションは、JNIのロギングを有効にします。JNIまたはネイティブ・メソッドが解決されると、HotSpot VMはアプリケーション・コンソール(標準出力)にトレース・メッセージを出力します。また、ネイティブ・メソッドがJNI RegisterNative関数を使用して登録されるときもトレース・メッセージを出力します。-verbose:jniオプションは、ネイティブ・ライブラリを使用するアプリケーションの問題を診断する際に役立つ可能性があります。

E このリリースのツールのサマリー

この付録では、現在のリリースのJDKで使用可能なツールのサマリーと、以前のリリースからの変更点を説明します。

このドキュメントに記載されているJDKトラブルシューティング・ツールはすべて、Oracle SolarisおよびLinux上のJDK 9で使用できます。

Windows上のJDK 9では、次のJDKトラブルシューティング・ツールも使用できます。

- Java Mission Control

- Javaフライト記録

- フライト記録の作成方法

- フライト記録の検査

- jcmd

- JConsole

- Java仮想マシン

- jdb

- jinfo

- jmap

- jps (現在Windows 98またはWindows MEでは使用できません)

- jrunscript

- jstack

- jstat (現在Windows 98またはWindows MEでは使用できません)

- jstatd (現在Windows 98またはWindows MEでは使用できません)

- visualgc (現在Windows 98またはWindows MEでは使用できません)

Oracleの法律上の注意点

コピーライト

Copyright © 1994-2018, Oracle and/or its affiliates. All rights reserved.

ライセンス制約の保証と結果的に生じる損害の免責

このソフトウェアおよび関連ドキュメントの使用と開示は、ライセンス契約の制約条件に従うものとし、知的財産に関する法律により保護されています。ライセンス契約で明示的に許諾されている場合もしくは法律によって認められている場合を除き、形式、手段に関係なく、いかなる部分も使用、複写、複製、翻訳、放送、修正、ライセンス供与、送信、配布、発表、実行、公開または表示することはできません。このソフトウェアのリバース・エンジニアリング、逆アセンブル、逆コンパイルは互換性のために法律によって規定されている場合を除き、禁止されています。

保証免責

ここに記載された情報は予告なしに変更される場合があります。また、誤りが無いことの保証はいたしかねます。誤りを見つけた場合は、オラクルまでご連絡ください。

制限付権利について

このソフトウェアまたは関連ドキュメントを、米国政府機関もしくは米国政府機関に代わってこのソフトウェアまたは関連ドキュメントをライセンスされた者に提供する場合は、次の通知が適用されます。

U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are "commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, use, duplication, disclosure, modification, and adaptation of the programs, including any operating system, integrated software, any programs installed on the hardware, and/or documentation, shall be subject to license terms and license restrictions applicable to the programs. No other rights are granted to the U.S. Government.

危険な用途への使用について

このソフトウェアまたはハードウェアは様々な情報管理アプリケーションでの一般的な使用のために開発されたものです。このソフトウェアまたはハードウェアは、危険が伴うアプリケーション(人的傷害を発生させる可能性があるアプリケーションを含む)への用途を目的として開発されていません。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用する際、安全に使用するために、適切な安全装置、バックアップ、冗長性(redundancy)、その他の対策を講じることは使用者の責任となります。このソフトウェアまたはハードウェアを危険が伴うアプリケーションで使用したことに起因して損害が発生しても、Oracle Corporationおよびその関連会社は一切の責任を負いかねます。

商標と登録商標について

OracleおよびJavaはオラクルおよびその関連会社の登録商標です。その他の社名、商品名等は各社の商標または登録商標である場合があります。

Intel、Intel Xeonは、Intel Corporationの商標または登録商標です。すべてのSPARCの商標はライセンスをもとに使用し、SPARC International, Inc.の商標または登録商標です。AMD、Opteron、AMDロゴ、AMD Opteronロゴは、Advanced Micro Devices, Inc.の商標または登録商標です。UNIXは、The Open Groupの登録商標です。

第三者のコンテンツ、製品、サービスに対する免責事項

このソフトウェアまたはハードウェア、そしてドキュメントは、第三者のコンテンツ、製品、サービスへのアクセス、あるいはそれらに関する情報を提供することがあります。適用されるお客様とOracle Corporationとの間の契約に別段の定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスに関して一切の責任を負わず、いかなる保証もいたしません。適用されるお客様とOracle Corporationとの間の契約に定めがある場合を除いて、Oracle Corporationおよびその関連会社は、第三者のコンテンツ、製品、サービスへのアクセスまたは使用によって損失、費用、あるいは損害が発生しても一切の責任を負いかねます。

Pre-General Availability (一般提供前)版のドキュメントについて

このドキュメントがPre-General Availability (一般提供前)版の場合:

このドキュメントはPre-General Availability (一般提供前)版であり、デモおよび暫定使用のみを目的としたものです。このソフトウェアを使用するハードウェアに限定するものではありません。Oracle Corporationおよびその関連会社は、このドキュメントに関して一切の責任を負わず、いかなる保証もいたしません。また、このドキュメントを使用したことによって損失、費用、あるいは損害が発生しても、一切の責任を負いかねます。

非公開のPre-General Availability (一般提供前)版のドキュメントについて

このドキュメントが非公開のPre-General Availability (一般提供前)版の場合:

このドキュメントに含まれる情報は、情報の共有のみを目的としたものであって、顧客諮問委員会(Customer Advisory Board)のメンバーとしてのお客様の立場でのみ、またはお客様のPre-General Availability Trial Agreement (Pre-GAトライアル契約)のみに従って検討されるべきものとします。マテリアル、コード、または機能の提供をお約束するものではなく、購入決定を行う際の判断材料とすべきではありません。このドキュメントに記載されている特徴または機能の開発、リリースおよび時期については、オラクルの裁量により決定されます。

このドキュメントには、ソフトウェアまたは印刷物などの形式を問わず、オラクルが独占的な権利を有する財産的情報が含まれています。この機密資料へのアクセスと使用は、お客様とオラクルとの間で締結され、お客様が遵守に同意した、Oracle Master Agreement、Oracle License and Oracle Services Agreement (オラクル・ライセンスおよびサービスに関する契約書)、Oracle Partner Network Agreement、Oracle Distribution Agreementまたはその他のライセンス契約の条件に従うものとします。このドキュメントとその内容の開示、コピー、複製および配布には、オラクルによる事前の承諾を必要とします。このドキュメントはライセンス契約の一部となるものではなく、オラクルおよびその子会社や関連会社との契約を構成するものではありません。

ドキュメントのアクセシビリティ

オラクルのアクセシビリティについての詳細情報は、Oracle Accessibility ProgramのWebサイト(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc)を参照してください。

Oracle Supportへのアクセス

サポートをご契約のお客様には、My Oracle Supportを通して電子支援サービスを提供しています。詳細情報は(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info)か、聴覚に障害のあるお客様は(http://www.oracle.com/pls/topic/lookup?ctx=acc&id=trs)を参照してください。