Go to Contents Go to Java Page
J2SE 1.5 虎の穴
 
 

JVM をかいま見る
Monitoring and Management for Java Platform

 
 
Tiger

Light Weight なモニタリングがしたい

 
 

JVM がどのような状態にあるかを調べることはアプリケーションには非常に重要です。

開発中であればメモリリークはないか、スレッドがデッドロックしていないかなどを調べることは不可欠です。運用中であってもメモリの使用量や CPU の使用量、スレッドの状態をモニタリングすることでアプリケーションが正常に動作しているか調べることができます。

実をいうと Tiger 以前でも、一般的にプロファイラと呼ばれるアプリケーションを使用することで JVM の状態を調べることは可能でした。市販の製品では Borland の Optimizeit や Quest Software の JProbe (日本での扱いは Tangent Computing もしくは グレープシティ) などがあります。フリーのものであれば、Eclipse Profiler Project や JMP などがあります。

これらのプロファイラを使えばとても詳しく JVM の状態をモニタリングすることができます。

しかし、問題があります。

プロファイルを一度使用されてみればすぐ分かるのですが、非常に重い。プロファイルを行っているせいで、アプリケーションの動作速度が非常に遅くなってしまうのです。

シングルスレッドのアプリケーションであればそれでもいいかもしれませんが、通信を行っていたり、マルチスレッドで動作のタイミングが重要なアプリケーションなどはプロファイリングを行っているせいで動作しなくなってしまうこともあります。

プロファイリングは非常に重要な機能でこれはこれで必要なのですが、もう少し機能を絞ってもいいのでもっと軽く JVM の様子をモニタリングするための機能がほしいところです。

そして、Tiger でその願いがやっとかなうことができました。それが JSR-174 で策定されている Monitoring and Management Specification for the Java Virtual Machine なのです。

JSR-174 は JMX をベースにしたフレームワークで、使い方は非常に簡単です。JVM の状態は MBean に保持されています。JMX をベースにしているということでローカルはもちろんリモートから JVM の状態を知ることもできます。

この機能を使用することで、サーバ系のアプリケーションの状態をリモートから調べるなんてこともできるのです。

まさに願っていた機能ではないでしょうか。

 

 
 
Tiger とりあえず使ってみる
 
 

Monitoring and Management for Java Platform で提供されている MBean は 9 種類あります。java.util.logging.LoggingMXBean 以外は java.lang.management パッケージで定義されています。

MBean 名称 説明
OperatingSystemMXBean OS に関する MBean
RuntimeMXBean クラスパスや起動時オプションなどのランタイムに関する MBean
CompilationMXBean JIT に関する MBean
ClassLoadingMXBean クラスローディングに関する MBean
ThreadMXBean スレッド数やスレッドの状態などスレッド全般に関する MBean
GarbageCollectorMXBean GC に関する MBean
MomoryMXBean メモリに関する概要を示す MBean
MomoryManagerMXBean メモリプールを統括して扱うための MBean
MomoryPoolMXBean 用途別に用意されるメモリプールの詳細に関する MBean
LoggingMXBean ロギングに関する MBean

これらの MBean は単一のオブジェクトで自分で生成することはできません。MBean の取得には java.lang.ManagementFactory を使用します。 たとえば、RuntimeMXBean を取得するには次のようにします。

サンプルのソース RuntimeMXBeanTest.java

ManagementFactory クラスには static な MBean の取得メソッドが定義されています。

        RuntimeMXBean mbean = ManagementFactory.getRuntimeMXBean();

このメソッドを使用した場合は、MBeanServer オブジェクトには登録されていません。ローカルで使用するならばこのメソッドで取得できた RuntimeMBean オブジェクトをそのまま使用してもいいのですが、やはり MBeanServer オブジェクトに登録してリモートからでも見れるようにしてみましょう。

登録するには MBean の名前が必要なのですが、名前は ManagementFactory クラスの定数としてすでに定義されているのでそれを使用するようにします。

        MBeanServer server = MBeanServerFactory.createMBeanServer();
        
        try {
            RuntimeMXBean mbean = ManagementFactory.getRuntimeMXBean();
            server.registerMBean(mbean,
                                 new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME));
            
            System.out.println("Creating an HTML protocol adaptor..");
            HtmlAdaptorServer adaptor = new HtmlAdaptorServer();
            ObjectName adaptorName = new ObjectName("Adaptor:name=adaptor,port=8082");
            server.registerMBean(adaptor, adaptorName);
            adaptor.start();
        } catch (InstanceAlreadyExistsException ex) {
            ex.printStackTrace();
        } catch (MBeanRegistrationException ex) {
            ex.printStackTrace();
        } catch (NotCompliantMBeanException ex) {
            ex.printStackTrace();
        } catch (MalformedObjectNameException ex) {
            ex.printStackTrace();
        }

RUNTIME_MXBEAN_NAME は具体的には java.lang:type=Runtime となっています。したがって、ドメインは java.lang です。他の java.lang.management パッケージで提供されている MBean のドメインは java.lang となっています。

これを実行してブラウザで確認してみましょう。

RuntimeMBeanTest
図 1 RuntimeMBeanTest の出力結果

RuntimeMXBean が登録されていることが確認できます。

このように 1 つ 1 つ MBean を取得して MBeanServer オブジェクトに登録してもいいのですが、ちょっと面倒です。だからというわけではないと思いますが、ManagementFactory#getPlatformMBeanServer メソッドという便利なメソッドが用意されています。

このメソッドを使ったサンプルが JVMMonitoringTest クラスです。

サンプルのソース JVMMonitoringTest.java

使い方はむちゃくちゃ簡単で、単にコールするだけでおしまいです。

    public JVMMonitoringTest() {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
 
        try {
            System.out.println("Creating an HTML protocol adaptor..");
            HtmlAdaptorServer adaptor = new HtmlAdaptorServer();
            ObjectName adaptorName = new ObjectName("Adaptor:name=adaptor,port=8082");
            server.registerMBean(adaptor, adaptorName);
            
            adaptor.start();
        } catch (MalformedObjectNameException ex) {
            ex.printStackTrace();
        } catch (NullPointerException ex) {
            ex.printStackTrace();
        } catch (InstanceAlreadyExistsException ex) {
            ex.printStackTrace();
        } catch (MBeanRegistrationException ex) {
            ex.printStackTrace();
        } catch (NotCompliantMBeanException ex) {
            ex.printStackTrace();
        }
    }

getPlatformMBeanServer メソッドの戻り値は MBeanServer オブジェクトですでに java.lang.management パッケージで定義されている MBean がすべて登録されています。

これを実行してみたのが、図 2 です。

JVMMonitoringTest
図 2 JVMMonitoringTest の出力結果

ずらっと MBean が並んでいるのを見ると壮観ですね。このように Monitoring and Management for Java Platform で定義された MBean を使用することはとても簡単です。

次章では、それぞれの MBean がどのような属性やオペレーション、ノティフィケーションを定義しているか 1 つ 1 つ確認していきましょう。

 

 
 
Tiger プラットフォームに関連する MBean
 
 

まずはプラットフォームに関連する MBean から見ていきます。

OperatingSystemMXBean

OperatingSystemMBean では次の情報を参照することができます。

OperatingSystemMXBean で定義されている属性
属性名 説明
Arch String マシンのアーキテクチャ
AvailableProcessors int 利用可能なプロセッサ数
Name String OS の名称
Version String OS のバージョン

ところが、ブラウザで見てみるとこれ以外の情報も参照することができます。調べてみると、java.lang.management.OperatingSystemMBean インタフェースを派生させた com.sun.management.OperatingSystemMBean インタフェースを使用しているようです。

追加された情報は

com.sun.management.OperatingSystemMXBean で定義されている属性
属性名 説明
TotalPhysicalMemorySize long 物理メモリ量
FreePhysicalMemorySize long 使用可能な物理メモリ量
TotalSwapMemorySize long スワップメモリ量
FreeSwapMemorySize long 使用可能なスワップメモリ量
CommittedVirtualMemorySize long JVM が使用しているメモリ量
ProcessCpuTime long プロセスの CPU 時間 単位は ms

ただし、ここで追加された情報はあくまでも Sun の HotSpot インプリメンテーションにおけるものなので、他社の Tiger に準拠した JavaVM でこれらの情報を参照できる保証はありません。

OperatingSystemMXBean
図 3 OperatingSystemMXBean

図 3 からはアーキテクチャが x86、OS が Windows XP でバージョンが 5.1 であることが分かります。使用したマシンはメモリが 768 MB、スワップ用に 372 MB とってあるので、図 3 の値とだいたい一致しますね。数値が一致していないのは、この PC がメインメモリの一部をグラフィックメモリとして使用しているからです。

 

 
 
Tiger 実行環境に関連する MBean
 
 

次はアプリケーションの実行環境に関する MBean です。

RuntimeMXBean

RuntimeMXBean はアプリケーションの実行時の環境に関する情報を提供します。参照できる情報は

RuntimeMXBean で定義されている属性
属性名 説明
VmName String JVM の名称
VmVendor String JVM のベンダ
VmVersion String JVM のバージョン
Name String 稼動中の JVM の名前 id@マシンネーム で表されるようです
SpecName String JVM のスペックの名称
SpecVendor String JVM のスペックのベンダ
SpecVersion String JVM のスペックバージョン
ManagementSpecVersion String マネージメントシステムのバージョン
BootClassPath String ブートクラスパス
ClassPath String クラスパス
BootClassPathSupported boolean ブートクラスパスのサポートの有無
LibraryPath String ライブラリパス
InputArguments List<String> 起動時オプション
SystemProperties Map<String, String> システムプロパティ
StartTime long JVM が起動した時間
Uptime long JVM が起動してからの時間 単位は ms

システムプロパティの型は Map<String, String> なのですが、HTML プロトコルアダプタだと TabularData とあらわされてしまいます。この TabularData は javax.management.openmbean パッケージで OpenMBean で使用されるインタフェースなのですが、これを派生させている TabularDataSupport クラスは Map インタフェースもインプリメントしているのでこのように表示されるのでしょう。

また、起動時オプションは JVM のオプションで main メソッドの引数 args ではないようです。こちらも型は List<String> なのですが、String[] になってしまっています。

RuntimeMXBean
図 4 RuntimeMXBean

モザイクがかかっていているのは、マシン固有のパスとかが入っているからです。普通に見たらもちろんちゃんと見えますよ。

 

CompilationMXBean

JIT に関する情報を保持する MBean です。閲覧できるのは

CompilationMXBean で定義されている属性
属性名 説明
Name String JIT の名称
CompilationTimeMonitoringSupported boolean コンパイルのモニタリングをサポートしているかどうか
TotalCompilationTime long トータルのコンパイル時間 単位は ms

 

CompilationMXBean
図 5 CompilationMXBean

ClassLoadingMXBean

ClassLoadingMXBean はクラスロードに関する情報を保持しています。

ClassLoadingMXBean で定義されている属性
属性名 説明
LoadedClassCount int ロードされているクラス数
UnloadedClassCount int アンロードされたクラス数
TotalLoadedClassCount long トータルのロードされたクラス数
Verbose boolean verbose の状態

verbose は起動時オプションの -verbose:class が指定されているかどうかを示しています。この属性は書きこみ可なので、途中から verbose をオンにすることもできます。

ClassLoadingMXBean
図 6 ClassLoadingMXBean

ロードされているクラスは 173 で、アンロードされたクラスはありません。ロードされているクラスが以外に少ないと思いませんか。そんなもんなのかな。

 

 
 
Tiger スレッドに関する MBean
 
 

スレッドに関する情報を扱うのは ThreadMXBean だけですが、かなりたくさんの情報を見ることができます。

ThreadMXBean

ThreadMBean を使用すれば、アプリケーションで動作しているすべてのスレッドの情報を見ることができます。

ThreadMBean の属性を次に示します。

ThreadMXBean で定義されている属性
属性名 説明
ThreadCount int 現在のスレッド数
DeamonThreadCount int デーモンスレッドのスレッド数
PeakThreadCount int スレッド数のピーク値
TotalStartedThreadCount long アプリケーションを起動してからの全スレッド数
CurrentThreadCpuTime long カレントスレッドの CPU 時間 単位は ナノ秒
CurrentThreadCputTimeSupported boolean カレントスレッドの CPU 時間をサポートするかどうか
ThreadCpuTimeEnabled boolean スレッドの CPU 時間をモニタリングするかどうか
ThreadCpuTimeSupported boolean スレッドの CPU 時間をサポートするかどうか
CurrentThreadUserTime long スレッドのユーザ時間
ThreadContentionMonitoringEnabled boolean スレッドの競合をモニタリングするかどうか
ThreadContentionMonitoringSupported boolean スレッドの競合をモニタリングをサポートするかどうか
AllThreadIds long[] スレッド ID の一覧

 

ThreadMXBean
図 7 ThreadMXBean

スレッドの CPU 時間とユーザ時間はナノ秒単位なのですが、その正確性は OS やプラットフォームに依存するようです。たまたまこのときのカレントスレッドは 0 ナノ秒になっています。

次はオペレーションです。

ThreadMXBean で定義されているオペレーション
オペレーション名 説明
void resetPeakThreadCount() ピークスレッド数のリセット
long getThreadUserTime(long id) スレッドのユーザ時間 単位はナノ秒
long getThreadCpuTime(long id) スレッドの CPU 時間 単位はナノ秒
TheadInfo getThreadInfo(long id) 指定したスレッドの情報取得
スタックトレースは含まれない
ThreadInfo getThreadInfo(long id, int maxDepth) 指定したスレッドの情報取得
引数 maxDepth はスタックトレースの深さ
TheadInfo[] getThreadInfo(long[] ids) 指定したスレッドの情報取得
スタックトレースは含まれない
ThreadInfo[] getThreadInfo(long[] ids, int maxDepth) 指定したスレッドの情報取得
引数 maxDepth はスタックとレースの深さ
long[] findMonitorDeadlockedThreads() デッドロックしているスレッドを調べる

getThreadInfo オペレーションの戻り値は ThreadInfo クラスです。このクラスはスレッドのさまざまな情報を保持しています。

ThreadInfo が保持している属性

属性名 説明
ThreadId long スレッドの ID
ThreadName String スレッドの名前
ThreadState ThreadState スレッドの状態 ThreadState で表される
Suspended boolean Thread#suspend メソッドが呼ばれている状態かどうか
InNative boolean スレッドが JNI を介してネイティブコードで動作しているかどうか
BlockedTime long 処理がブロックされていた時間
値が -1 の時は ContentionMonitoringEnabled が true になっていないか、ContentionMonitoring がサポートされていない
単位は ms
BlockedCount long 処理がブロックされた回数
WaitedTime long 処理が WAITING もしくは TIMED_WAITING であった時間
値が -1 の時は ContentionMonitoringEnabled が true になっていないか、ContentionMonitoring がサポートされていない
単位はミリ秒
WaitedCount long 処理が待ち状態にあった回数
LockName String BLOCKED や WAITING もしくは TIMED_WAITING の状態の時の、ロックの名前
ロックのクラス名 @ ハッシュ値 であらわされる
LockOwnerId long ロックを持っているスレッドの ID
値が -1 の場合、ロック状態でない
LockOwnerName String ロックを保持しているスレッドの名前
StackTrace StackTraceElement[] スタックトレース
スタックトレースの深さは getThradInfo メソッドの引数で指定する

getThreadState オペレーションの戻り値は Thread クラスの内部 enum の Thread.State になります。Thread.State は次の状態を示すことができます。

Thread.State で定義される定数
定数名 説明
NEW 生成されているが、まだ起動していない状態
RUNNING 動作中
BLOCKED synchronized ブロックに入るためにブロックされている状態
WAITING Object#wait() や LockSupport#park() メソッドで notify を待っている状態
TIMED_WAITING Object#wait(long timeout) など、タイムアウトを指定されて notify を待っている状態
SLEEPING Thread#sleep がコールされている状態
TERMINATED スレッドが終了した状態

残念ながら HTML プロトコルアダプタは ThreadInfo クラスをサポートしていないのでブラウザではこれらの情報を見ることができません。そこで、次のようなサンプルを作ってみました。

サンプルのソース ThreadMXBeanTest.java
CompactGridLayout.java

ThreadMBean オブジェクトから現在存在しているすべてのスレッドに関する ThreadInfo オブジェクトを取得し、これを GUI で表示するというサンプルです。

ThreadInfo オブジェクトは getThreadInfo(long[] ids, int maxDepth) メソッドを使用して、まとめて取得してもいいのですが、CPU 時間が ThradInfo オブジェクトに含まれないので、次のようにループして取得するようにしました。

スタックトレースは 5 行分取得するようにしています。

    private void updateInfo() {
        long[] ids = mbean.getAllThreadIds();
        Arrays.sort(ids);
 		
        for (long id: ids) {
            long cputime = mbean.getThreadCpuTime(id);
            ThreadInfo info = mbean.getThreadInfo(id, 5);
            addInfoTab(cputime, info);
        }
    }

addInfoTab メソッドでスレッドの情報を表示するのですが、画面を構成しているのは addInfoTab メソッドからコールされる updateInfoTab メソッドです。updateInfoTab メソッドではまずスレッドの情報を取得し、それを JLabel クラスを使用して画面に表記しています。

    private void updateInfoTab(JPanel panel, long cputime, ThreadInfo info) {
        long id = -1;
        String name = "";
        Thread.State state = null;
        boolean isSuspended = false;
        boolean isInNative = false;
        long blockedTime = -1;
        long blockedCount = -1;
        long waitedTime = -1;
        long waitedCount = -1;
        String lockName = "";
        long lockOwnerId = -1;
        String lockOwnerName = "";
        String stackTrace = "";
 
        synchronized (info) {
            id = info.getThreadId();
            name = info.getThreadName();
            state = info.getThreadState();
            isSuspended = info.isSuspended();
            isInNative = info.isInNative();
            blockedTime = info.getBlockedTime();
            blockedCount = info.getBlockedCount();
            waitedTime = info.getWaitedTime();
            waitedCount = info.getWaitedCount();
            lockName = info.getLockName();
            lockOwnerId = info.getLockOwnerId();
            lockOwnerName = info.getLockOwnerName();
            
            StringBuffer buffer = new StringBuffer();
            buffer.append("<html><body>");
            for (StackTraceElement element: info.getStackTrace()) {
                buffer.append(element.toString());
                buffer.append("<br/>");
            }
            buffer.append("</body></html>");
            stackTrace = buffer.toString();
        }
 
        ... 以下省略 ...

このように synchronized ブロックを使用しているのは、ThreadInfo オブジェクトに対応してスレッドが終了してしまうと ThreadInfo オブジェクトも消滅してしまう null になってしまうことがあるためです。

実行すると次のようなフレームが描画されます。

ThreadMBeanTest
図 8 ThreadMBeanTest の出力結果

これを見ていると、いろいろと興味深いです。たとえば、AWT-Windows スレッドはネイティブコードで実行されているとか。WAITING 状態にあるスレッドと RUNNNIG 状態にあるスレッドなど、など。

Update ボタンで情報を更新できるので、更新すると main スレッドが終了していることが分かります。その他、いろいろ発見があるかもしれません。

 

 
 
Tiger メモリに関する MBean
 
 

最後に残ったのが一番の大物、メモリに関する MBean です。メモリに関する MBean には MemoryMXBean, MemoryPoolMXBean, MemoryManagerMXBean, GarbageCollectorMXBean と 4 種類もあります。

メモリに関する MBean を試す前に現在の HotSpot におけるメモリマネージメントを少しだけおさらいしておきます。

HotSpot のメモリマネージメント

JVM が使用するメモリ領域にはヒープ領域とノンヒープ領域に分けることができます。すべてのオブジェクトはヒープ領域にアロケートされます。ノンヒープ領域は Java Virtual Machine Specification では Method 領域と呼ばれていますが、コンスタントプールやメソッドテーブルなどクラスごとにあるデータを配置します。

それぞれの領域のサイズは固定ではなく、増減することが可能です。

HotSpotのメモリ構成
図 9 HotSpotのメモリ構成

ヒープ領域にはオブジェクトがアロケートされますが、アロケートする領域が不足した場合 Garbage Collection を行い不要なオブジェクトを廃棄します。

HotSpot はこの GC の手法として Generational GC を採用しています。この手法はオブジェクトの寿命 (オブジェクトが生成されてから消滅するまでの期間) に着目したものです。Generation GC ではヒープを世代ごとに次に示す 2 つの領域に分割しています。

  • Young Generation
  • Old Generation

ノンヒープ領域はこの Generation にたとえて Permanent Generation と呼ばれます。

オブジェクトが生成されるとまず Young Generation にアロケートされます。Young Generation に新たにオブジェクトをアロケートする領域がなくなると、Young Generation だけを対象にした GC が行われます。

GC を何度も生き抜いたオブジェクトは寿命が長いとみなされるので Young Generation から Old Generation にコピーされます。

Old Generation にコピーされたオブジェクトも使われなくなることがあります。また、新たに Young Generation からオブジェクトがコピーされきます。このようなオブジェクトが溜まってきてしきい値を超えると、Young/Old を問わずすべての領域を対象にした GC が行われます。

java のオプションで -verbose:gc をつけると GC が行われる様子が分かります。[GC 837K->353K(1984K), 0.0015108 secs] と表記されるものが Young Generation を対象とした GC を表しており、[Full GC 513K->276K(1984K), 0.5010827 secs] と表記されたときがすべての領域を対象に GC が行われたことを表しています (もちろん、メモリのサイズや時間は毎回異なります)。

これが基本なのですが、HotSpot では効率化のために Young Generation をさらに Eden, Survivor 1, Survivor 2 と呼ばれる 3 つの領域に分割しています。Old Generation は 1 つのままですが Tenured と呼ばれるようです。

オブジェクトが最初にアロケートされるのが Eden です。GC が行われると Eden にあるオブジェクトは Suvivor 1 にコピーされます。このとき Survivor 2 は使用されません。この GC で Eden は空になります。

次の GC では Eden と Survivor 1 のオブジェクトが Survivor 2 にコピーされます。このコピーで Eden と Survivor 1 は空になります。

このように Survivor を交互に使用するのがこの方法の特徴です。Eden も Survivor も 1 度の GC で空になりことから、効率よく高速に行うことが可能です。

Old Generation の GC には Mostly Concurrent Mark & Sweep 方式が使用されますが、ここでは省略します。詳しくは ISMM 2000 で発表された A Generational Mostly-concurrent Garbage Collector をご覧ください。

 

MemoryMXBean

本題に戻りましょう。まずはメモリに関して一番基本となる MemoryMXBean からです。MemoryMXBean を使用することでヒープ領域とノンヒープ領域の使用状況を調べることが可能です。

MemoryMXBean の属性
属性名 説明
HeapMemoryUsage MemoryUsage ヒープ領域の使用状況
NonHeapMemoryUsage MemoryUsage ノンヒープ領域の使用状況
ObjectPendingFinalizaionCount int finalize 処理がペンディングしているオブジェクトの概算の数
Verbose boolean -verbose:gc が設定してあるかどうか

Verbose だけが書き込み可能なので、後から verbose を設定することも可能です。

MemoryMBean で定義されているオペレーションには gc があります。名前のとおり GC を行うものです。

MemoryMXBean で定義されているオペレーション
オペレーション名 説明
void gc() GC を行う

属性の HepMemoryUsage と NonHeapMemoryUsage がメモリ領域の使用状況を表すものです。メモリの使用状況には MemoryUsage クラスを使用してます。

MemoryUsage クラスには 4 つの属性があり、単位はすべてバイトです。

MemoryUsage の属性
属性名 説明
init long 初期状態で割り当てられるメモリ量
used long 現在、使用しているメモリ量
committed long 現在、使用可能なメモリ量 used <= committed の関係になる
committed の値は増減し、init 以下になることもあるが、max を超えることはない
max long JVM が割り当てるメモリの最大値
ThreadMBeanTest
図 10 MemoryUsage

残念ながら MemoryUsage クラスは HTML プロトコルアダプタでサポートしていない型なので、次のようなサンプルを作ってみました (ただし、HTML プロトコルアダプタでも MemoryUsage#toString メソッドを使用しているので属性の値を見ることは可能です)。

サンプルのソース MemoryMXBeanTest.java
MemoryUsageGraph.java
CompactGridLayout.java

500ms ごとに HeapMemoryUsage と NonHeapMemoryUsage を取得してメモリの使用量をグラフに表示します。500ms ごとにコールされるのが MemoryMXBeanTest#updateView メソッドです。heapGraph と nonheapGraph は MemoryUsageGraph オブジェクトです。

    private void updateView() {
        MemoryUsage usage = mbean.getHeapMemoryUsage();
        heapGraph.setMemoryUsage(usage);
        heapLabel.setText(HEAP 
                      + "(" + format.format(usage.getMax()/TRANS_B_TO_M) + "M, "
                      + format.format(usage.getCommitted()/TRANS_B_TO_K) + "K): "
                      + format.format(usage.getUsed()/TRANS_B_TO_K) + "K");
 
        usage = mbean.getNonHeapMemoryUsage();
        nonheapGraph.setMemoryUsage(usage);
        nonheapLabel.setText(NON_HEAP 
                      + "(" + format.format(usage.getMax()/TRANS_B_TO_M) + "M, "
                      + format.format(usage.getCommitted()/TRANS_B_TO_M) + "M): "
                      + format.format(usage.getUsed()/TRANS_B_TO_M) + "M");
    }

 

MemoryMBeanTest
図 11 MemoryMBeanTest の出力結果

表示の中にある数字は左から max, committed, used の順です。この表示は -Xmx4m というオプションで起動させています。-Xmx はヒープの最大を決めるためのオプションなのですが、なぜかヒープの最大は 3.94 MB。なぜなんでしょう。

 

MemoryPoolMXBean

MemoryMXBean を使えば、ヒープとノンヒープという 2 つの領域の情報を得られることが分かりました。しかし、前述したようにこれらのメモリ領域はいくつかのサブ領域に分割されて使用されています。これらの領域がメモリプールですが、その状況を見るために使用されるのが MemoryPoolMXBean です。

MemoryPoolMXBean で定義されている属性を次に示します。

MemoryPoolMXBean の属性
属性名 説明
Name String メモリプール領域の名称
Type MemoryType タイプ (ヒープ or ノンヒープ)
Valid boolean 有効かどうかをしめすフラグ
MemoryManagerName String[] この領域を管理しているMemoryManagerの名称
1 つとは限らない
Usage MemoryUsage メモリプールの使用状況
UsageThreshold long メモリプールの used のしきい値
UsageThresholdSupported boolean UsageThreshold をサポートしているかどうか
UsageThresholdCrossed boolean used が UsageThreshold を超えているかどうか
UsageThresholdCount long used が UsageThreshold を超えた回数
PeakUsage MemoryUsage used がピークの時の使用状況
CollectionUsage MemoryUsage GC が行われた後の、メモリプールの使用状況
CollectionUsageThreshold long GC が行われた後の、メモリプールの used のしきい値
CollectionUsageThresholdSupported boolean GC が行われた後の、CollectionUsageThreshold をサポートしているかどうか
CollectionUsageThresholdCrossed boolean GC が行われた後の、used が CollectionUsageThreshold を超えているかどうか
CollectionUsageThresholdCount long GC が行われた後の、used が CollectionUsageThreshold を超えた回数

メモリの使用状況に関して Usage というのと CollectionUsage というのがあるのがお分かりだと思います。この 2 つの違いは Usage が現在のメモリ使用状況なのに対し、CollectionUsage は GC が終わったあとのメモリ使用量ということです。

それぞれしきい値 (UsageThreshold もしくは CollectionUsageThreshold) を設定することができます。これを使えば、しきい値を超えているかどうか、また超えた回数などを調べることができます。

MemoryPoolMBean で定義されているオペレーションを次に示します。

MemoryPoolMXBean で定義されているオペレーション
オペレーション名 説明
void resetPeakUsage() 属性 PeakUsage をリセットする

Tiger では Young Generation で 3 メモリプール、Old Generation で 1 メモリプール、Permanent Generation で 4 つのメモリプールがあります。それぞれのメモリプールの情報を調べるために次のようなサンプルを作ってみました。

サンプルのソース MemoryPoolMXBeanTest.java
MemoryUsageGraph.java
CompactGridLayout.java

やっていることは ThreadMXBeanTest クラスとあまり変わりません。MemoryPoolMXBean から情報を取得して GUI で描画しているだけです。

MemoryPoolMXBeanTest の出力結果
図 12 MemoryPoolMXBeanTest の出力結果

beta 1 では Survivor 領域はちゃんと 2 つとして認識されていたのですが、beta 2 では 1 つとして数えられているようです。まぁ、一方を使っているときは、もう一方は空なので 1 つでいいのかもしれません。

なぜかよく分からないのですが、Eden は UsageThreasholdSupported が false になっています。たしかに Eden にしきい値をつける必要は全然ないと思いますが、よく分かりません。

このように UsageThresholdSupported や CollectionUsageSupported が false になる場合、UsageThresholdExceeded と UsageThresholdCount などの属性を取得しようとすると例外が発生してしまうので、注意が必要です。

 

MemoryManagerMXBean, GarbageCollectorMXBean

MemoryManagerMXBean はメモリプールの管理情報に関する MBean です。GarbageCollector もメモリを管理しているので、GarbageCollectorMXBean は MemoryManagerMXBean の派生インタフェースとなっています。

MemoryManagerMXBean だけではあまり面白くないので、ここでは GarbageCollectorMXBean を取りあげましょう。

GarbageCollectorMXBean で定義されている属性を次に示します。

GarbageCollectorMXBean の属性
属性名 説明
CollectionCount long GC の回数
CollectionTime long GC にかかった時間の概算値

これ以外に MemoryManagerMXBean で定義されている属性もあります。

MemoryManagerMXBean の属性
属性名 説明
Name String MemoryManager の名称
MemoryPoolNames List 管理しているメモリプールの名前一覧
Valid boolean 有効かどうかを示すフラグ

ところが GarbageCollectorMXBean も OperatingSystemMXBean のように拡張されているようです。実際には com.sun.management.GarbageCollectorMXBean が使用されています。ここで定義されている属性は次のようなものがあります。

com.sun.management.GarbageCollectorMXBean の属性
属性名 説明
LastGCInfo GCInfo 一番最近に行った GC に関する情報

LastGCInfo の型である GCInfo クラスはやはり com.sun.management パッケージにあります。GCInfo クラスには次に示すメソッドが用意されています。

GCInfo クラスで定義されている主なメソッド
メソッド名 説明
long getID() GC の ID
long getStartTime() GC が開始された時間
long getEndTime() GC が終了した時間
long getDuration() GC に要した時間 単位は ms
Map getMeoryUsageBeforeGc() GC が行われる前の各メモリプールのメモリ使用状況
Map のキーはメモリプールの名称、値は MemoryUsage オブジェクト
Map getMemoryUsageAfterGc() GC が行われた後の各メモリプールのメモリ使用状況
Map のキーはメモリプールの名称、値は MemoryUsage オブジェクト

GCInfo クラスを扱えるようなサンプルということで今までほとんど同じですが、GarbageCollectorMXBeanTest クラスを作ってみました。

サンプルのソース GarbageCollectorMXBeanTest.java
CompactGridLayout.java

実行結果を図 13 に示します。

GarbageCollectorMXBeanTest の出力結果
図 13 GarbageCollectorMXBeanTest の出力結果

このぐらいの小さいアプリケーションだと Mark & Sweep の GC はなかなか発生しないので、強引に GC をさせるために GC ボタンをつけてあります。この GC ボタンがクリックされると MemoryMXBean#gc メソッドをコールするようにしてあります。

 

メモリに関するノティフィケーション

メモリの使用量はアプリケーションを書くときに気になるところです。予想外にメモリ使用量が多かったり、メモリリークが起こっていたりしたらと思うと気が気ではありません。

そんなあなたに効果的なのがノティフィケーションです。JSR-174 で定義されているノティフィケーションは 2 つあり、両方ともメモリに関するものです。

  1. メモリの使用量がしきい値を超えた場合
  2. GC の後にメモリの使用量がしきい値を超えた場合

MemoryPoolMBean の属性に UsageThreashold と CollectionUsageThreshold というのがありましたが、これがノティフィケーションの発生にかかわってくるしきい値になります。

UsageThreshold を超えた場合、1 のノティフィケーション。CollectionUsageThreshold を超えた場合、2 のノティフィケーションになります。

こう聞くと、 リスナ登録をするのは MemoryPoolMBean のような気がしますが、実際には MemoryMBean になります。メモリに関するものは MemoryMBean でまとめて扱おうということなのだと思います。

さて、ノティフィケーションを扱うサンプルを作ってみましょう。先ほどの、MemoryPoolMBeanTest クラスを拡張してノティフィケーションを扱えるようにしてみます。

サンプルのソース MemoryNotificationTest.java
MemoryUsageGraph.java
CompactGridLayout.java

メモリに関するノティフィケーションは通常の Notification クラスを使用します。メモリに関する情報を引っ張り出すには Notification#getUserData メソッドを使用します。 リスナ登録は MemoryNotificationTest クラスのコンストラクタで行っているので、その部分の抜きだしてみましょう。

        NotificationListener listener = new NotificationListener() {
            public void handleNotification(Notification notification, Object handback)  {
                String type = notification.getType();
                if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)
                    || type.equals(MemoryNotificationInfo.MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) {
 
                    CompositeData cd = (CompositeData)notification.getUserData();
                    MemoryNotificationInfo info = MemoryNotificationInfo.from(cd);
                    
                    String poolName = info.getPoolName();
                    long count = info.getCount();
                    MemoryUsage usage = info.getUsage();
                    String message = "Type : " + type
                        + "\nMemory Pool Name: " + poolName
                        + "\nUsage: " + createUsageText(usage);
                    
                    JOptionPane.showMessageDialog(frame, message,
                                                  "Memory Warning", 
                                                  JOptionPane.WARNING_MESSAGE);
                }
            }
        };
 
        MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
        NotificationEmitter emitter = (NotificationEmitter)mbean;
        emitter.addNotificationListener(listener, null, null);

ノティフィケーションが発生したら、Notification#getType メソッドでそのノティフィケーションが何かを調べます。メモリに関するノティフィケーションは MemoryNotificationInfo クラスで定義されている MEMORY_THRESHOLD_EXCEED か MEMORY_COLLECTION_THRESHOLD_EXCEEDED になります。

Notification#getUserData メソッドの戻り値は Object クラスなのですが、上記の 2 つのタイプの場合は javax.management.openmbean.CompositeData クラスになります。CompositeData クラスは複合データ用の汎用クラスなので、そのままだと少し使いにくいのです。

そこで、メモリに関するノティフィケーションに特化した java.lang.management.MemoryNotificationInfo クラスを使用します。CompositeData クラスから MemoryNotificationInfo クラスに変換するには、static な from メソッドを使用します。

MemoryNotificationInfo クラスは次のような情報を保持しています。

  • ノティフィケーションが発生したメモリプール名
  • しきい値を超えた回数
  • メモリの使用状況

上記のコードではプール名と使用状況をダイアログで出力しています。

リスナ登録を行うには MemoryMXBean オブジェクトを NotificationEmitter オブジェクトにキャストしてから行います。

MemoryNotificationTest クラスを実行するには、引数が 2 つ必要です。第 1 引数が UsageThreshold、第 2 引数が CollectionUsageThreshold になります。両方とも Tenured Gen に設定しました。

        List pools = ManagementFactory.getMemoryPoolMXBeans();
        for (MemoryPoolMXBean pool: pools) {
            if (pool.getName().indexOf("Tenured") != -1) {
                pool.setUsageThreshold(usageThreshold);
                pool.setCollectionUsageThreshold(collectionUsageThreshold);
                break;
            }
        }

ノティフィケーションが発生した時は図 14 のようになります。このときは CollectionUsageThreshold を 1MB に設定して実行させたところです。

MemoryNotificationTest の出力結果
図 14 MemoryNotificationTest の出力結果

 

 
 
Tiger ロギングに関する MBean
 
 

最後に残ったのがちょっと毛色の変わったロギングに関する MBean である LoggingMXBean です。この MBean だけ java.util.logging パッケージで定義されています。

LoggngMXBean で定義されている属性
属性名 説明
LoggerNames List<String> ロガーの名前の一覧

 

LoggingMXBean
図 15 LoggingMXBean

view the values o LoggerNames をクリックしてみると次のようになりました。

ロガーの一覧
図 16 ロガーの一覧

LoggingMXBean にはオペレーションは次の 3 つが定義されています。

LoggngMXBean で定義されているオペレーション
オペレーション名 説明
String getLoggerLevel(String loggerName) 指定したロガーのレベル
void setLoggerLevel(String loggerName, String levelName) ロガーのレベル設定
String getParentLoggerName(String loggerName) 指定したロガーの親ロガーの取得

この MBean を使えば、アプリケーションを動作中にロガーのレベルを帰ることができるので、運用開始時はレベルを下げておいて、問題なかったらレベルを上げるなんてことが可能になります。

でも、もっとカスタマイズできるといいと思うのは私だけではないと思うのですが。特にハンドラーに関する属性やオペレーションなどがあればいいと思います。たとえば、ハンドラーのレベルを変えたり、後からハンドラーを追加したりしたりできたらいいと思いませんか。

 

 
 
Tiger Out-of-the-Box な管理
 
 

今までの例はすべてアプリケーションのコードの中に MBean を作成、登録するコードを書いていました。しかし、既存のアプリケーションを管理したい場合はどうしましょう。

Monitoring and Management for Java Platform にはそれに対する解も提供しているのです。

やり方は簡単で、起動オプションをちょっと加えるだけなのです。

ローカルでの管理

まずはローカルのマシンでやってみましょう。とはいっても同じ JVM 上で管理するわけではなく、アプリケーションと管理アプリケーションは別々のプロセスとして起動します。

管理するアプリケーションはトリビアアプリケーション、管理アプリケーションには J2SDK に付属する jconsole を使用します。

管理するためには java の起動オプションとして -Dcom.sun.management.jmxremote をつけて起動します。

C:\examples>java -Dcom.sun.management.jmxremote -cp trivia.jar;jmxtools.jar jp.g
r.java_conf.skrb.game.trivia.server.TriviaServer

2 行になってしまっていますが、実際には 1 行で入力しています。

起動したら、他のコンソールから jps コマンドを起動します。このコマンドは起動している JVM の pid を出力します。この中から TriviaServer の pid を探し出して、その値を jconsole コマンドの引数にします。

C:\examples>jps                                                                 
3132 TriviaServer
1380 Jps
3408 TriviaClient
3456 TriviaClient C:\examples>jconsole 3132

jconsole を起動すると図 17 のようなウィンドウが表示されます。

jconsole
図 17 jconsole (クリックすると拡大します)

jconsole は Monitoring and Management for Java Platform に特化しているので、他の MBean は見ることはできません。でも、HTML プロトコルアダプタで見るよりはずっと使いやすいインタフェースになっています。

気をつけなくてはいけないことは、ローカルで jconsole を使うときアプリケーションと同じユーザアカウントで行う必要があることです。

 

RMI を使用したリモートでの管理

JMX Remote を使用すればリモートから RMI を使用してアプリケーションの管理ができます。Monitoring and Management for Java Platform でも RMI のコネクタを使用できるので、リモートから管理を行うことができます。

RMI を使って管理するためには java の起動オプションとして -Dcom.sun.management.jmxremote.port=[ポート番号] をつけて起動します。デフォルトでは SSL が使用されるので、SSL を使用しない場合には -Dcom.sun.management.jmxremote.ssl=false オプションも付加します。

下の例ではポート 8000 を使用した例です。起動すると RMI コネクタを起動したというログがコンソールに出力されます。

C:\examples>java -Dcom.sun.management.jmxremote.port=8000 -Dcom.sun.management.j
mxremote.ssl=false -cp trivia.jar;jmxtools.jar jp.gr.java_conf.skrb.game.trivia.
server.TriviaServer
2004/06/01 01:02:50 ConnectorBootstrap initialize
情報: JMX Connector ready at: service:jmx:rmi://ZAZAZ/jndi/rmi://ZAZAZ:8000/jmxr
mi

ここで出力されている URL でリモートからアクセスするようにします。サーバの名前は jmxr に固定されてしまうようです。

jconsole ではこのデフォルトの名前が分かっているため ホストネーム:ポート でアクセスできます。

C:\examples>jconsole ZAZAZ:8000                                                 

そうすると先ほどと同じウィンドウが表示されるはずです。

ここでは SSL を使用しないことを直接オプションで記述しましたが、設定ファイルで設定する方法もあります。設定ファイルを読み込むには次のオプションを指定します。

    -Dcom.sun.management.config.file=[設定ファイルのパス]

デフォルトでは jre/lib/management/management.properties が使用されます。

その他にもパスワードなどオプションがあるのですが、詳しくは Out-of-the-Box Remote Monitoring and Management のドキュメントをご覧ください。

また、jconsole や jps などのツールに関しては Tools and Utilities のドキュメント をご参照ください。

 

SNMP を使用したリモートでの管理

なんと Tiger では SNMP プロトコルアダプターも標準で使えるようになっています。

ただし、パッケージは com.sun.jmx.snmp なので標準というわけではありません。また、ドキュメントも Monitoring and Management for Java Platform に関する部分しかないので、自由にアプリケーションの中で使うというわけにはいかなそうです。

SNMP で管理をする場合も SNMP コネクタを起動するポートを指定するだけです。

    -Dcom.sun.management.snmp.port=[ポート番号]

通常 SNMP では 161 が使われます。SNMP での設定ファイルの指定は RMI と同じです。デフォルトの設定ファイルも jre/lib/management/management.properties が使用されます。

また、コミュニティなどを記述した ACL (Access Control List) ファイルを指定する場合は

    -Dcom.sun.management.snmp.acl.file=[ACL ファイルのパス]

ACL がよく分からない場合は、ACL を無効にしてしまうこともできます。

    -Dcom.sun.management.snmp.acl=false

ただし、このオプションを指定した場合はどこからでもアクセスできてしまうので、テストの時以外は使わないほうがいいと思います。

さて、SNMP でアクセスするには oid が必要です。oid が分からなければどこにもアクセスできません。まずはどんな oid があるか調べるために、net-snmp を使ってすべての oid を取得してみました。実行は Fedora Core 1 で行っています。

[sakuraba]$ snmpwalk -v 2c -c public udp:10.0.0.10 .1
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.1.1.0 = Gauge32: 1658
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.1.2.0 = Counter64: 1658
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.1.3.0 = Counter64: 0
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.1.4.0 = INTEGER: 1
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.1.0 = Gauge32: 0
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.2.0 = INTEGER: 1
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.3.0 = INTEGER: 2
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.10.0 = Counter64: 0
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.11.0 = Counter64: 1135560
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.12.0 = Counter64: 2097152
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.13.0 = Counter64: 66650112
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.20.0 = Counter64: 29556736
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.21.0 = Counter64: 14653288
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.22.0 = Counter64: 30113792
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.23.0 = Counter64: 121634816
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.100.1.2.1 = STRING: "CodeCacheManager"
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.100.1.2.2 = STRING: "Copy"
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.100.1.2.3 = STRING: "MarkSweepCompact"
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.100.1.3.1 = INTEGER: 2
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.100.1.3.2 = INTEGER: 2
SNMPv2-SMI::enterprises.42.2.145.3.163.1.1.2.100.1.3.3 = INTEGER: 2

     ... 以下、省略 ...

詳しい MIB の定義は JVM-MANAGEMENT-MIB.mib に記載されているのですが、oid の名前と型を抜粋したのが下の表です。どの oid が、どの MBean の属性に対応するかはだいたい名前からお分かりだと思うので記述しませんでした。

もしかしたら抜けや間違いがあるかもしれませんが、もし見つけたらご連絡ください。

Management and Monitoring for Java Platform で使用されている MIB のoid
oid の名前 oid
jvmOSProcessorCount 1.3.6.1.4.1.42.2.145.3.163.1.1.6.4 Integer32
jvmOSVersion 1.3.6.1.4.1.42.2.145.3.163.1.1.6.3 String
jvmOSArch 1.3.6.1.4.1.42.2.145.3.163.1.1.6.2 String
jvmOSName 1.3.6.1.4.1.42.2.145.3.163.1.1.6.1 String
jvmJITCompilerTimeMonitoring 1.3.6.1.4.1.42.2.145.3.163.1.1.5.3 Integer32
jvmJITCompilerTimeMs 1.3.6.1.4.1.42.2.145.3.163.1.1.5.2 Counter64
jvmJITCompilerName 1.3.6.1.4.1.42.2.145.3.163.1.1.5.1 String
jvmRTLibraryPathTable 1.3.6.1.4.1.42.2.145.3.163.1.1.4.23 Table
jvmRTLibraryPathEntry 1.3.6.1.4.1.42.2.145.3.163.1.1.4.23.1 Table Entry
jvmRTLibraryPathItem 1.3.6.1.4.1.42.2.145.3.163.1.1.4.23.1.2 String
jvmRTLibraryPathIndex 1.3.6.1.4.1.42.2.145.3.163.1.1.4.23.1.1 Integer32
jvmRTClassPathTable 1.3.6.1.4.1.42.2.145.3.163.1.1.4.22 Table
jvmRTClassPathEntry 1.3.6.1.4.1.42.2.145.3.163.1.1.4.22.1 Table Entry
jvmRTClassPathItem 1.3.6.1.4.1.42.2.145.3.163.1.1.4.22.1.2 String
jvmRTClassPathIndex 1.3.6.1.4.1.42.2.145.3.163.1.1.4.22.1.1 Integer32
jvmRTBootClassPathTable 1.3.6.1.4.1.42.2.145.3.163.1.1.4.21 Table
jvmRTBootClassPathEntry 1.3.6.1.4.1.42.2.145.3.163.1.1.4.21.1 Table Entry
jvmRTBootClassPathItem 1.3.6.1.4.1.42.2.145.3.163.1.1.4.21.1.2 String
jvmRTBootClassPathIndex 1.3.6.1.4.1.42.2.145.3.163.1.1.4.21.1.1 Integer32
jvmRTBootClassPathSupport 1.3.6.1.4.1.42.2.145.3.163.1.1.4.9 Integer32
jvmRTInputArgsTable 1.3.6.1.4.1.42.2.145.3.163.1.1.4.20 Table
jvmRTInputArgsEntry 1.3.6.1.4.1.42.2.145.3.163.1.1.4.20.1 Table Entry
jvmRTInputArgsItem 1.3.6.1.4.1.42.2.145.3.163.1.1.4.20.1.2 String
jvmRTInputArgsIndex 1.3.6.1.4.1.42.2.145.3.163.1.1.4.20.1.1 Integer32
jvmRTManagementSpecVersion 1.3.6.1.4.1.42.2.145.3.163.1.1.4.8 String
jvmRTSpecVersion 1.3.6.1.4.1.42.2.145.3.163.1.1.4.7 String
jvmRTSpecVendor 1.3.6.1.4.1.42.2.145.3.163.1.1.4.6 String
jvmRTSpecName 1.3.6.1.4.1.42.2.145.3.163.1.1.4.5 String
jvmRTVMVersion 1.3.6.1.4.1.42.2.145.3.163.1.1.4.4 String
jvmRTVMVendor 1.3.6.1.4.1.42.2.145.3.163.1.1.4.3 String
jvmRTStartTimeMs 1.3.6.1.4.1.42.2.145.3.163.1.1.4.12 Counter64
jvmRTUptimeMs 1.3.6.1.4.1.42.2.145.3.163.1.1.4.11 Counter64
jvmRTVMName 1.3.6.1.4.1.42.2.145.3.163.1.1.4.2 String
jvmRTName 1.3.6.1.4.1.42.2.145.3.163.1.1.4.1 String
jvmRTInputArgsCount 1.3.6.1.4.1.42.2.145.3.163.1.1.4.10 Integer32
jvmThreadCpuTimeMonitoring 1.3.6.1.4.1.42.2.145.3.163.1.1.3.6 Integer32
jvmThreadContentionMonitoring 1.3.6.1.4.1.42.2.145.3.163.1.1.3.5 Integer32
jvmThreadTotalStartedCount 1.3.6.1.4.1.42.2.145.3.163.1.1.3.4 Counter64
jvmThreadPeakCount 1.3.6.1.4.1.42.2.145.3.163.1.1.3.3 Counter
jvmThreadDaemonCount 1.3.6.1.4.1.42.2.145.3.163.1.1.3.2 Gauge32
jvmThreadCount 1.3.6.1.4.1.42.2.145.3.163.1.1.3.1 Gauge32
jvmThreadInstanceTable 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10 Table
jvmThreadInstanceEntry 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1 Table Entry
jvmThreadInstName 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.9 String
jvmThreadInstCpuTimeNs 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.8 Counter64
jvmThreadInstWaitTimeMs 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.7 Counter64
jvmThreadInstWaitCount 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.6 Counter64
jvmThreadInstBlockTimeMs 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.5 Counter64
jvmThreadInstBlockCount 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.4 Counter64
jvmThreadInstState 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.3 String
jvmThreadInstLockOwnerPtr 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.11 OID
jvmThreadInstId 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.2 Counter64
jvmThreadInstLockName 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.10 String
jvmThreadInstIndex 1.3.6.1.4.1.42.2.145.3.163.1.1.3.10.1.1 String
jvmThreadPeakCountReset 1.3.6.1.4.1.42.2.145.3.163.1.1.3.7 Counter64
jvmMemMgrPoolRelTable 1.3.6.1.4.1.42.2.145.3.163.1.1.2.120 Table
jvmMemMgrPoolRelEntry 1.3.6.1.4.1.42.2.145.3.163.1.1.2.120.1 Table Entry
jvmMemMgrRelPoolName 1.3.6.1.4.1.42.2.145.3.163.1.1.2.120.1.3 String
jvmMemMgrRelManagerName 1.3.6.1.4.1.42.2.145.3.163.1.1.2.120.1.2 String
jvmMemoryNonHeapMaxSize 1.3.6.1.4.1.42.2.145.3.163.1.1.2.23 Counter64
jvmMemoryNonHeapCommitted 1.3.6.1.4.1.42.2.145.3.163.1.1.2.22 Counter64
jvmMemoryNonHeapUsed 1.3.6.1.4.1.42.2.145.3.163.1.1.2.21 Counter64
jvmMemPoolTable 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110 Table
jvmMemPoolEntry 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1 Table Entry
jvmMemPoolCollectMaxSize 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.33 Counter64
jvmMemPoolCollectCommitted 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.32 Counter64
jvmMemPoolCollectUsed 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.31 Counter64
jvmMemPoolCollectThreshdSupport 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.133 Integer32
jvmMemPoolCollectThreshdCount 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.132 Counter64
jvmMemPoolCollectThreshold 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.131 Counter64
jvmMemPoolMaxSize 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.13 Counter64
jvmMemPoolCommitted 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.12 Counter64
jvmMemPoolUsed 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.11 Counter64
jvmMemPoolInitSize 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.10 Counter64
jvmMemPoolThreshdSupport 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.112 Integer32
jvmMemPoolThreshdCount 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.111 Counter64
jvmMemPoolThreshold 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.110 Counter64
jvmMemPoolPeakReset 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.5 Counter64
jvmMemPoolState 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.4 Integer32
jvmMemPoolType 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.3 Integer32
jvmMemPoolName 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.2 String
jvmMemPoolPeakMaxSize 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.23 Counter64
jvmMemPoolIndex 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.1 Integer32
jvmMemPoolPeakCommitted 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.22 Counter64
jvmMemPoolPeakUsed 1.3.6.1.4.1.42.2.145.3.163.1.1.2.110.1.21 Counter64
jvmMemoryNonHeapInitSize 1.3.6.1.4.1.42.2.145.3.163.1.1.2.20 Counter64
jvmMemoryHeapMaxSize 1.3.6.1.4.1.42.2.145.3.163.1.1.2.13 Counter64
jvmMemoryHeapCommitted 1.3.6.1.4.1.42.2.145.3.163.1.1.2.12 Counter64
jvmMemoryGCCall 1.3.6.1.4.1.42.2.145.3.163.1.1.2.3 Integer32
jvmMemoryHeapUsed 1.3.6.1.4.1.42.2.145.3.163.1.1.2.11 Counter64
jvmMemoryGCVerboseLevel 1.3.6.1.4.1.42.2.145.3.163.1.1.2.2 Integer32
jvmMemGCTable 1.3.6.1.4.1.42.2.145.3.163.1.1.2.101 Table
jvmMemGCEntry 1.3.6.1.4.1.42.2.145.3.163.1.1.2.101.1 Table Entry
jvmMemGCTimeMs 1.3.6.1.4.1.42.2.145.3.163.1.1.2.101.1.3 Counter64
jvmMemGCCount 1.3.6.1.4.1.42.2.145.3.163.1.1.2.101.1.2 Counter64
jvmMemoryHeapInitSize 1.3.6.1.4.1.42.2.145.3.163.1.1.2.10 Counter64
jvmMemoryPendingFinalCount 1.3.6.1.4.1.42.2.145.3.163.1.1.2.1 Gauge32
jvmMemManagerTable 1.3.6.1.4.1.42.2.145.3.163.1.1.2.100 Table
jvmMemManagerEntry 1.3.6.1.4.1.42.2.145.3.163.1.1.2.100.1 Table Entry
jvmMemManagerState 1.3.6.1.4.1.42.2.145.3.163.1.1.2.100.1.3 Integer32
jvmMemManagerName 1.3.6.1.4.1.42.2.145.3.163.1.1.2.100.1.2 String
jvmMemManagerIndex 1.3.6.1.4.1.42.2.145.3.163.1.1.2.100.1.1 Integer32
jvmClassesVerboseLevel 1.3.6.1.4.1.42.2.145.3.163.1.1.1.4 Integer32
jvmClassesUnloadedCount 1.3.6.1.4.1.42.2.145.3.163.1.1.1.3 Counter64
jvmClassesTotalLoadedCount 1.3.6.1.4.1.42.2.145.3.163.1.1.1.2 Counter64
jvmClassesLoadedCount 1.3.6.1.4.1.42.2.145.3.163.1.1.1.1 Gauge32
jvmLowMemoryPoolUsageNotif 1.3.6.1.4.1.42.2.145.3.163.1.2.2.1.0.1 Notification
jvmLowMemoryPoolCollectNotif 1.3.6.1.4.1.42.2.145.3.163.1.2.2.1.0.2 Notification

 

 
 
Tiger おわりに
 
 

Monitoring and Management for Java Platform はこれを記述している時点では、まだまだ仕様が確定していないようです。alpha の時には影も形もなく、beta 1 になって登場し、beta 2 でクラス名をはじめかなりの部分が変更されています。

J2SE 1.4 の時の Logging API がこんな感じでした。書いても書いてもまた修正しなくてはならなかったのを思いだします。できれば、Monitoring and Management for Java Platform ではそんなことがないようにしてもらいたいものですが、どうなることやら。

仕様変更に関してはともかく、便利であることは間違いありません。

私が愛用しているツールの 1 つに jvmstat というのがあります。このツールを使うとメモリの詳細が分かるのです。残念なことにこのツールは J2SE 1.4 専用なのです。

しかし、Monitoring and Management for Java Platform を使えば jvmstat のようなものはすぐ作れてしまうのです。J2SDK には jconsole というツールも付属しています。また、MC4J のように Tiger に対応した JMX 管理アプリケーションも出てきています。

いつでもどこでも好きなように JVM の中を覗けるなんて、なんてすばらしいんでしょう。

 

今回使用したサンプルはここからダウンロードできます。

 

参考

(Jun. 2004)

 
 
Go to Contents Go to Java Page