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

3.2 OutOfMemoryError例外の理解

メモリー・リークの一般的な兆候の1つは、java.lang.OutOfMemoryError例外です。通常、このエラーは、Javaヒープにオブジェクトを割り当てるための十分な空間がないときにスローされます。この場合、ガベージ・コレクタによって新しいオブジェクトを格納するための空間を確保することも、ヒープをこれ以上拡張することもできません。また、このエラーは、Javaクラスのロードをサポートするための十分なネイティブ・メモリーがないときにスローされる場合もあります。ガベージ・コレクションの実行に過剰な時間が消費され、メモリーがほとんど解放されていない場合、まれにjava.lang.OutOfMemoryErrorがスローされることがあります。

java.lang.OutOfMemoryError例外がスローされるときに、スタック・トレースも出力されます。

java.lang.OutOfMemoryError例外は、ネイティブ割当てを満たすことができないとき(たとえば、スワップ空間が少ない場合)に、ネイティブ・ライブラリ・コードによってスローされることもあります。

OutOfMemoryError例外を診断する最初の手順は、その例外の原因を突き止めることです。スローされた理由が、Javaヒープがいっぱいであるからなのか、それともネイティブ・ヒープがいっぱいであるからなのかを調べます。原因を見つけることができるように、例外テキストの最後に詳細なメッセージが含まれています(次の例外を参照)。

Exception in thread thread_name: java.lang.OutOfMemoryError: Java heap space
原因: 詳細メッセージのJavaヒープ領域は、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は致命的エラー処理メカニズムを呼び出します(つまり、クラッシュ発生時のスレッド、プロセス、およびシステムに関する有用な情報を含む、致命的エラー・ログ・ファイルを生成します)。ネイティブ・ヒープ不足の場合は、ログ内のヒープ・メモリーとメモリー・マップの情報が役立つ可能性があります。致命的エラー・ログ・ファイルの理解の詳細は、付録Aを参照してください。

このタイプの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)またはネイティブ・メソッドで検出されたことです。
目次      

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