3.1.1 詳細メッセージ: Java heap space
3.1.3 詳細メッセージ: Requested array size exceeds VM limit
3.2 OutOfMemoryError ではなくクラッシュ
3.3.3.4 -XX:+HeapDumpOnOutOfMemoryError コマンド行オプション
3.3.5 OutOfMemoryError 時のヒープヒストグラムの取得
アプリケーションの実行時間がしだいに長くなる場合や、オペレーティングシステムの実行速度がしだいに低下しているように思われる場合、これはメモリーリークの兆候である可能性があります。つまり、仮想メモリーが割り当てられているけれども、それが不要になったときに返されません。最終的には、アプリケーションまたはシステムがメモリーを使い果たし、アプリケーションが異常終了します。
この章では、メモリーリークが関与する問題の診断について、いくつかのアドバイスを行います。
メモリーリークの共通の兆候の 1 つは、java.lang.OutOfMemoryError エラーです。このエラーは、Java ヒープまたはヒープの特定の領域にオブジェクトを割り当てるための十分な空間がないときにスローされます。ガベージコレクタによって新しいオブジェクトを格納するための空間をこれ以上確保することも、ヒープをこれ以上拡張することもできません。
java.lang.OutOfMemoryError エラーがスローされると、スタックトレースも出力されます。
java.lang.OutOfMemoryError は、ネイティブ割り当てを満たすことができないとき (たとえば、スワップ空間が少ない場合) に、ネイティブライブラリコードによってスローされることもあります。
OutOfMemoryError を診断する最初の手順は、エラーの意味を知ることです。Java ヒープがいっぱいであることを意味するのか、またはネイティブヒープがいっぱいであることを意味するのか。この質問に答えられるように、以降のサブセクションでは、考えられるエラーメッセージの一部について説明し、メッセージの詳細部分を参照します。
Exception in thread "main": java.lang.OutOfMemoryError: Java heap space
「3.1.1 詳細メッセージ: Java heap space」を参照してください。
Exception in thread "main": java.lang.OutOfMemoryError: PermGen space
「3.1.2 詳細メッセージ: PermGen space」を参照してください。
Exception in thread "main": java.lang.OutOfMemoryError: Requested array size exceeds VM limit
「3.1.3 詳細メッセージ: Requested array size exceeds VM limit」を参照してください。
Exception in thread "main": java.lang.OutOfMemoryError: request
「3.1.4 詳細メッセージ: request
Exception in thread "main": java.lang.OutOfMemoryError:
「3.1.5 詳細メッセージ:
詳細メッセージ Java heap space は、Java ヒープ内でオブジェクトを割り当てることができなかったことを示します。このエラーは、必ずしもメモリーリークを意味しません。この問題は、指定されたヒープサイズ (指定されていない場合はデフォルトサイズ) がアプリケーションにとって十分でないという、単純な構成の問題である可能性があります。
それ以外の場合、特に長期にわたって稼動するアプリケーションでは、このメッセージはアプリケーションが誤ってオブジェクトへの参照を保持しているため、オブジェクトのガベージコレクションができないことを示している可能性があります。これは、Java 言語ではメモリーリークに相当します。アプリケーションによって呼び出された API が誤ってオブジェクト参照を保持している可能性もあります。
OutOfMemoryError のもう 1 つの潜在的な原因は、ファイナライザを過度に使用するアプリケーションで発生します。クラスに finalize メソッドがある場合、そのタイプのオブジェクトの空間はガベージコレクション時に再利用されません。代わりに、ガベージコレクション後にファイナライズ用のキューにオブジェクトが入れられ、あとでファイナライズが実行されます。Sun の実装では、ファイナライザはファイナライズキューを提供するデーモンスレッドによって実行されます。ファイナライザスレッドがファイナライズキューを処理しきれなかった場合は、Java ヒープがいっぱいになる可能性があり、OutOfMemoryError がスローされます。この状況を発生させるシナリオの 1 つは、アプリケーションが優先度の高いスレッドを作成しているため、ファイナライザスレッドがファイナライズキューを処理する速度よりキューの増加速度の方が速くなっている場合です。セクション「3.3.6 ファイナライズ保留中のオブジェクト数のモニタリング」では、ファイナライズが保留されているオブジェクトをモニターする方法について説明します。
詳細メッセージ PermGen space は、Permanent 世代がいっぱいであることを示します。Permanent 世代とは、ヒープ内で、クラスやメソッドのオブジェクトが格納される領域のことです。非常に多数のクラスをロードするアプリケーションでは、-XX:MaxPermSize オプションを使って Permanent 世代のサイズを増やす必要がある場合があります。
intern された java.lang.String オブジェクトは、Permanent 世代に格納されなくなります。java.lang.String クラスは文字列のプールを保持します。intern メソッドを呼び出すと、このメソッドはプールをチェックし、同等の文字列がすでにプール内に存在するかどうかを確認します。存在する場合、intern メソッドはそれを返し、そうでない場合はその文字列をプールに追加します。より正確には、java.lang.String.intern メソッドを使用して文字列の正規表現が取得されます。この結果は、その文字列がリテラルとして表示された場合に返される同じクラスインスタンスへの参照です。
この種類のエラーが発生した場合は、出力されるスタックトレースの最上部近くにテキスト ClassLoader.defineClass が表示されます。
jmap -permgen コマンドは、Permanent 世代に含まれるオブジェクトの統計を出力し、内部化された String インスタンスに関する情報も出力します。「2.7.4 Permanent 世代に関する情報の取得」を参照してください。
詳細メッセージ Requested array size exceeds VM limit は、アプリケーション (またはそのアプリケーションが使用する API) がヒープサイズより大きい配列を割り当てようとしたことを示します。たとえば、アプリケーションが 512M バイトの配列を割り当てようとしたが、最大ヒープサイズが 256M バイトだった場合は、理由 Requested array size exceeds VM limit とともに OutOfMemoryError がスローされます。ほとんどの場合、この問題は構成の問題 (ヒープサイズが小さすぎる) であるか、アプリケーションが非常に大きい配列を作成しようとする結果をもたらすバグ (たとえば、不正なサイズを計算するアルゴリズムを使用して配列内の要素の数が計算された場合など) です。
詳細メッセージ: request
このエラーメッセージがスローされると、VM は致命的エラー処理メカニズムを呼び出します (つまり、クラッシュ発生時のスレッド、プロセス、およびシステムに関する有用な情報を含む、致命的エラーログファイルを生成します)。ネイティブヒープ不足の場合は、ログ内のヒープメモリーとメモリーマップの情報が役立つ可能性があります。このファイルの詳細は、付録 C「致命的エラーログ」を参照してください。
このタイプの OutOfMemoryError がスローされた場合は、オペレーティングシステムのトラブルシューティングユーティリティーを使用して、問題をより詳しく診断する必要がある場合があります。「2.16 オペレーティングシステム固有のツール」を参照してください。
問題がアプリケーションに関連していない可能性もあります。例を示します。
オペレーティングシステムに十分なスワップ空間が構成されていない。
システム上の別のプロセスがすべてのメモリーリソースを消費している。
上記のどちらの問題も原因でない場合は、ネイティブのリークのためにアプリケーションが失敗した可能性があります (たとえば、アプリケーションまたはライブラリのコードが継続的にメモリーを割り当てているが、それをオペレーティングシステムに解放していない場合)。
エラーメッセージの詳細部分が
このタイプの OutOfMemoryError がスローされた場合は、オペレーティングシステムのユーティリティーを使用して、問題をより詳しく診断する必要がある場合があります。「2.16 オペレーティングシステム固有のツール」を参照してください。
ネイティブヒープからの割り当てが失敗した直後に、アプリケーションがクラッシュすることがあります。これは、メモリー割り当て関数によって返されたエラーをチェックしないネイティブコードで発生します。
たとえば、malloc システムコールは使用可能なメモリーがない場合に NULL を返します。malloc からの戻り値がチェックされない場合、アプリケーションは無効なメモリー位置にアクセスしようとしてクラッシュする可能性があります。状況によっては、このタイプの問題を特定することが難しいことがあります。
しかし、致命的エラーログやクラッシュダンプの情報があれば、この問題を十分に診断できる場合もあります。致命的エラーログについては、付録 C「致命的エラーログ」で詳しく説明されています。割り当ての失敗をチェックしていないことがクラッシュの原因であるとわかった場合は、割り当ての失敗の理由を調べる必要があります。ネイティブヒープに関するほかの問題と同様に、システムに十分なスワップ空間が構成されていなかったり、システム上の別のプロセスがすべてのメモリーリソースを消費していたり、システムのメモリー不足を引き起こすリークがアプリケーション内 (またはそこから呼び出された API 内) で発生していたりする可能性があります。
Java 言語コード内のリークの診断は、難しいタスクになる可能性があります。ほとんどの場合、アプリケーションの非常に詳しい知識が必要です。さらに、プロセスが反復し、冗長なことがよくあります。このセクションには、次のサブセクションがあります。
NetBeans Profiler (従来は JFluid と呼ばれていた) は、メモリーリークを迅速に特定できる優れたプロファイラです。商用のメモリーリークデバッグツールのほとんどは、多くの場合、大規模なアプリケーション内のリークを特定するのに長い時間がかかる場合があります。しかし、NetBeans Profiler は、このようなオブジェクトが一般的に示すメモリーの割り当てと再利用のパターンを使用します。このプロセスには、メモリー再利用の欠落も含まれます。このプロファイラは、これらのオブジェクトがどこで割り当てられたかをチェックできます (リークの根本原因を特定するには、多くの場合、これで十分です)。
詳細は、http://profiler.netbeans.org で見つけることができます。
jhat ユーティリティー (「2.5 jhat ユーティリティー」を参照) は、意図しないオブジェクトの保持 (またはメモリーリーク) をデバッグするときに役立ちます。オブジェクトダンプを参照し、ヒープ内の到達可能なすべてのオブジェクトを表示し、どの参照がオブジェクトを保持しているかを理解するための方法を提供します。
jhat を使用するには、実行中のアプリケーションのヒープダンプを 1 つ以上取得する必要があり、ダンプはバイナリ形式である必要があります。ダンプを作成したあとは、「2.5 jhat ユーティリティー」で説明しているように、それを jhat の入力として使用できます。
ヒープダンプは、ヒープメモリーの割り当てに関する詳細情報を提供します。以降のセクションでは、ヒープダンプを作成するいくつかの方法について説明します。
HPROF プロファイラエージェントは、アプリケーションの実行中にヒープダンプを作成できます。コマンド行の例を次に示します。
$ java -agentlib:hprof=file=snapshot.hprof,format=b application
VM が埋め込まれているか、追加オプションを指定できるコマンド行起動ツールを使用して起動されていない場合は、JAVA_TOOLS_OPTIONS 環境変数を使用して -agentlib オプションをコマンド行に自動的に追加できる可能性があります。この環境変数の詳細は、「A.2 JAVA_TOOL_OPTIONS 環境変数」を参照してください。
アプリケーションが HPROF とともに実行されている場合は、アプリケーションコンソールで (プラットフォームに応じて) Ctrl + \ または Ctrl + Break を押すことでヒープダンプが作成されます。Solaris OS および Linux では、代わりに kill -QUIT pid コマンドを使用して QUIT シグナルを送信する方法もあります。このシグナルを受信すると、ヒープダンプが作成されます。上の例では、snapshot.hprof ファイルが作成されます。
ヒープダンプファイルには、すべてのプリミティブデータとスタックトレースが含まれています。
1 つのダンプファイルに複数のヒープダンプを含めることができます。Ctrl + \ または Ctrl + Break を何度も押すと、後続のダンプがファイルの末尾に追加されます。jhat ユーティリティーでは、#n 構文 (n はダンプの番号) を使用してダンプを区別します。
jmap ユーティリティー (「2.7 jmap ユーティリティー」を参照) を使用してヒープダンプを取得することもできます。コマンド行の例を次に示します。
$ jmap -dump:format=b,file=snapshot.jmap process-pid
jmap ツールは、Java VM の起動方法に関係なくヒープダンプのスナップショット (上の例では snapshot.jmap という名前のファイルに) を生成します。jmap の出力ファイルにはすべてのプリミティブデータが含まれるはずですが、オブジェクトが作成された場所を示すスタックトレースは含まれません。
ヒープダンプを取得するもう 1 つの方法は、JConsole ユーティリティーを使用することです。「MBeans」タブで HotSpotDiagnostic MBean を選択すると、「Operations」が表示されるので、dumpHeap 操作を選択します。
-XX:+HeapDumpOnOutOfMemoryError コマンド行オプションを指定すると、OutOfMemoryError がスローされた場合に VM がヒープダンプを生成します。
ヒープヒストグラムを調べることで、メモリーリークをすばやく絞り込んでみることができます。この情報は、複数の方法で取得できます。
jmap -histo pid コマンドを使用して、実行中のプロセスからヒープヒストグラムを取得できます。この出力には、ヒープ内の各クラス型の合計サイズとインスタンス数が表示されます。ヒストグラムを連続して (たとえば、2 分ごとに) 取得すると、詳細な分析につなげることができる傾向を観察できる場合があります。
Solaris OS および Linux では、jmap ユーティリティーを使用してコアファイルからヒストグラムを提供することもできます。
-XX:+PrintClassHistogram コマンド行オプションを指定して Java プロセスを起動すると、Ctrl + Break ハンドラによってヒープヒストグラムが生成されます。
-XX:+HeapDumpOnOutOfMemoryError コマンド行オプションを指定すると、OutOfMemoryError がスローされた場合に VM がヒープダンプを生成します。その後、jmap ユーティリティーを使用してヒープダンプからヒストグラムを取得できます。
OutOfMemoryError がスローされたときにコアファイルが生成された場合は、次の例のようにコアファイルに対して 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[] :
この例は、OutOfMemoryError の原因が java.lang.String オブジェクトの数 (ヒープ内の 3611828 個のインスタンス) であることを示しています。文字列がどこで割り当てられたかは、詳細な分析を行わないとはっきりしません。それでもなおこの情報は有用で、HPROF や jhat などのツールを使用して引き続き調査を行うことで、文字列がどこで割り当てられたかや、どの参照によってそれらが保持され、ガベージコレクトされなくなっているかを特定できます。
「3.1.1 詳細メッセージ: Java heap space」で説明したように、ファイナライザを過度に使用すると、OutOfMemoryError が発生することがあります。ファイナライズを保留しているオブジェクトの数をモニターするためのオプションが複数用意されています。
JConsole 管理ツール (「2.3 JConsole ユーティリティー」を参照) を使用して、ファイナライズを保留しているオブジェクトの数をモニターできます。このツールでは、「Summary」タブペインのメモリー統計に保留中のファイナライズの数が報告されます。この数は概算ですが、アプリケーションを特徴付け、アプリケーションがファイナライズに大きく依存しているかどうかを理解するために使用できます。
Solaris OS および Linux では、jmap -finalizerinfo オプションによって、ファイナライズを待機しているオブジェクトに関する情報が出力されます。
アプリケーションは、java.lang.management.MemoryMXBean クラスの getObjectPendingFinalizationCount メソッドを使用して、ファイナライズを保留しているオブジェクトのおよその数を報告できます。API ドキュメントとコード例へのリンクは、「2.17 診断ツールの開発」で見つけることができます。コード例を拡張して、保留中のファイナライズ数の報告を簡単に追加できます。
前の章で説明したツールに加えて、数多くのサードパーティーメモリーデバッガが利用可能です。メモリーデバッグ機能を備えた商用ツールの例として、Quest Software 社の JProbe と Borland 社の OptimizeIt の 2 つがあります。ほかにも数多くありますが、推奨される特定の製品はありません。
複数の手法を使用してネイティブコードのメモリーリークを検出し、切り分けることができます。一般に、すべてのプラットフォームに対応する単独の理想的な解決方法はありません。
非常によく使われる方法は、ネイティブ割り当てのすべての割り当て呼び出しと解放呼び出しを追跡することです。これは、かなり簡単なプロセスになる場合と、非常に高度なプロセスになる場合があります。ネイティブヒープの割り当てとそのメモリーの使用の追跡をめぐっては、長年にわたって多くの製品が構築されています。
Purify や Sun の dbx の実行時チェック機能 (「3.4.4 dbx を使ったリークの検出」を参照) を使用すると、ネイティブコードの通常の状況におけるこれらのリークを検出したり、初期化されてないメモリーへの割り当てや解放されたメモリーへのアクセスを表すネイティブヒープメモリーへのアクセスを検出したりできます。
これらのタイプのツールのすべてがネイティブコードを使用する Java アプリケーションで機能するわけではなく、これらのツールは通常はプラットフォーム固有です。仮想マシンはコードを実行時に動的に作成するため、これらのツールはコードを誤って解釈したり、まったく動作しなかったり、誤った情報を与えたりする可能性があります。ツールのベンダーに問い合わせて、ツールのバージョンが、使用している仮想マシンのバージョンで動作することを確認してください。
http://sourceforge.net/ に、簡単で移植可能なネイティブメモリーリークの検出例が数多く見つかります。これらのライブラリやツールのほとんどは、アプリケーションのソースを再コンパイルまたは編集して、割り当て関数にラッパー関数を被せることができることを前提としています。より強力なツールでは、これらの割り当て関数に動的に介入することで、アプリケーションを変更せずに実行できます。これは、Solaris 9 OS update 3 で導入された libumem.so ライブラリにも当てはまります (「3.4.5 libumem を使ったリークの検出」を参照)。
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 の場合は、http://msdn.microsoft.com/library/default.asp にアクセスしてデバッグサポートを検索してください。Microsoft C++ コンパイラには、メモリー割り当てを追跡するための追加サポートを自動的に取り込む、/Md および /Mdd コンパイラオプションがあります。
Linux システムには、割り当て追跡を扱う際に役立つ、mtrace や libnjamd などのツールがあります。
Solaris オペレーティングシステムには、watchmalloc ツールが用意されています。Solaris 9 OS update 3 から libumem ツールの提供を開始しました (「3.4.5 libumem を使ったリークの検出」を参照)。
Sun のデバッガ dbx には、リークを検出できる実行時チェック (RTC) 機能が含まれています。dbx デバッガは 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 コマンドを使用しました。
Solaris 9 OS update 3 以降では、libumem.so ライブラリとモジュラーデバッガ (mdb) を使用してメモリーリークをデバッグできます。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 でメモリーリークを検出するコマンドです。リークが検出されたら、findleaks コマンドによって割り当て呼び出しのアドレス、バッファーアドレス、およびもっとも近いシンボルを出力します。
bufctl 構造体をダンプして、メモリーリークが発生した割り当てのスタックトレースを取得することもできます。この構造体のアドレスは、::findleaks コマンドの出力から取得できます。これらの機能を実行するコマンドの説明、および libumem を使ったメモリー管理バグの識別方法の詳細は、次のアドレスにあります: http://download.oracle.com/docs/cd/E19424-01/820-4814/geogv/index.html。