プラットフォーム MBean サーバーとプラットフォーム MXBean の使用
この章では、Java Platform, Standard Edition (Java SE プラットフォーム) の一部として提供される MBean サーバーと MXBean について説明します。MBean サーバーと MXBean は、監視および管理の目的で使用できます。Java Management Extensions (JMX) テクノロジの MBean と MBean サーバーについては、第 1 章「Java SE 監視および管理の概要」に概要が記されています。JMX テクノロジについて詳しくは、Java SE プラットフォームの JMX テクノロジのドキュメントを参照してください。
プラットフォーム MBean サーバーの使用
MBean サーバーは、管理アプリケーションから MBean にアクセスできるようにする MBean のリポジトリです。アプリケーションは、MBean に直接アクセスしませんが、その一意の ObjectName を介して、MBean サーバー経由で MBean にアクセスします。MBean サーバーには、インタフェース javax.management.MBeanServer が実装されています。
プラットフォーム MBean サーバーは、Java 2 Platform, Standard Edition 5.0 に導入され、Java 仮想マシン (Java VM) に組み込まれた MBean サーバーとなっています。プラットフォーム MBean サーバーは、Java VM で実行する、現在管理下にあるすべてのコンポーネントで共有できます。java.lang.management.ManagementFactory のメソッド getPlatformMBeanServer を使用して、プラットフォーム MBean サーバーにアクセスします。もちろん javax.management.MBeanServerFactory クラスを使用して独自の MBean サーバーを構築することもできます。ただし、通常、MBean サーバーが複数存在する必要はないため、プラットフォーム MBean サーバーの使用をお勧めします。
プラットフォーム MXBean へのアクセス
プラットフォーム MXBean は、Java VM の監視および管理を実施する MBean です。各 MXBean では、VM 機能の一部をカプセル化します。第 1 章「Java SE 監視および管理の概要」の表 1-1 にプラットフォームで提供する MXBean をすべて一覧で示しています。
管理アプリケーションでは、次の 3 種類の方法で、プラットフォーム MXBean にアクセスできます。
-
直接アクセス (ManagementFactory クラス経由)。
-
直接アクセス (MXBean プロキシ経由)。
-
間接アクセス (MBeanServerConnection クラス経由)。
次の 3 つのセクションでは、プラットフォーム MXBean へのこれら 3 種類のアクセス方法について説明します。
ManagementFactory クラスを使用したプラットフォーム MXBean へのアクセス
アプリケーションは、自身の Java VM と同一 Java VM で稼働するプラットフォーム MXBean のメソッドに対して直接呼び出しを行うことができます。直接呼び出しには、ManagementFactory クラスの static メソッドを使用できます。ManagementFactory には、getClassLoadingMXBean()、getGarbageCollectorMXBeans()、getRuntimeMXBean() などのさまざまなプラットフォーム MXBean にそれぞれ対応するアクセス用メソッドが用意されています。プラットフォーム MXBean が複数存在する場合、そのメソッドは検出したプラットフォーム MXBean のリストを返します。
たとえば、例 4-1 では、ManagementFactory の static メソッドを使用してプラットフォーム MXBean の RuntimeMXBean を取得し、さらにプラットフォーム MXBean からベンダー名を取得します。
例 4-1 ManagementFactory クラスを経由したプラットフォーム MXBean へのアクセスRuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean(); String vendor = mxbean.getVmVendor();
MXBean プロキシを経由したプラットフォーム MXBean へのアクセス
アプリケーションでは、MXBean プロキシを介してプラットフォーム MXBean メソッドを呼び出すこともできます。これを行うには、static メソッド ManagementFactory.newPlatformMXBeanProxy() を呼び出して、指定された MBean サーバーにメソッドの呼び出しを転送する MXBean プロキシインスタンスの作成が必要となります。アプリケーションでは、通常別の Java VM のプラットフォーム MXBean に対するリモートアクセスを可能とするプロキシを作成します。
たとえば、例 4-2 では、例 4-1 とまったく同じ処理を実行しますが、今回は MXBean プロキシを使用します。
例 4-2 MXBean プロキシを経由したプラットフォーム MXBean へのアクセスMBeanServerConnection mbs; ... // Get a MBean proxy for RuntimeMXBean interface RuntimeMXBean proxy = ManagementFactory.newPlatformMXBeanProxy(mbs, ManagementFactory.RUNTIME_MXBEAN_NAME, RuntimeMXBean.class); // Get standard attribute "VmVendor" String vendor = proxy.getVmVendor();
MBeanServerConnection クラスを使用したプラットフォーム MXBean へのアクセス
アプリケーションは、別の実行中の Java VM のプラットフォーム MBean サーバーに接続されている MBeanServerConnection を経由してプラットフォーム MXBean メソッドを間接的に呼び出すことができます。MBeanServerConnection クラスの getAttribute() メソッドを使用してプラットフォーム MXBean の属性を取得し、MBean の ObjectName と属性名をパラメータとして指定します。
たとえば、例 4-3 では、例 4-1 および例 4-2 と同じジョブを実行しますが、MBeanServerConnection を介して間接的な呼び出しを行います。
例 4-3 MBeanServerConnection クラスを経由したプラットフォーム MXBean へのアクセスMBeanServerConnection mbs; ... try { ObjectName oname = new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME); // Get standard attribute "VmVendor" String vendor = (String) mbs.getAttribute(oname, "VmVendor"); } catch (....) { // Catch the exceptions thrown by ObjectName constructor // and MBeanServer.getAttribute method ... }
Sun Microsystems のプラットフォーム拡張機能の使用
Java VM では、プラットフォームに特化した測定に必要なインタフェースと管理操作を定義することにより管理インタフェースを拡張できます。ManagementFactory クラスの static ファクトリメソッドは、プラットフォーム拡張機能を備えた MBean を返します。
com.sun.management パッケージには、Sun Microsystems のプラットフォーム拡張機能が含まれています。次のセクションでは、Sun Microsystems 実装 (OperatingSystemMXBean) からプラットフォーム固有の属性にアクセスする方法の例について説明します。
MXBean の各種属性に対する直接アクセス
例 4-4 では、Sun Microsystems の MXBean インタフェースの 1 つに対して直接アクセスを行なっています。
例 4-4 MXBean 属性に対する直接アクセスcom.sun.management.OperatingSystemMXBean mxbean = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); // Get the number of processors int numProcessors = mxbean.getAvailableProcessors(); // Get the Sun-specific attribute Process CPU time long cpuTime = mxbean.getProcessCpuTime();
MBeanServerConnection 経由による MXBean 属性へのアクセス
例 4-5 では、Sun Microsystems の MXBean インタフェースの 1 つに MBeanServerConnection クラス経由でアクセスします。
例 4-5 MBeanServerConnection を経由した MXBean 属性へのアクセスMBeanServerConnection mbs; // Connect to a running Java VM (or itself) and get MBeanServerConnection // that has the MXBeans registered in it ... try { // Assuming the OperatingSystem MXBean has been registered in mbs ObjectName oname = new ObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME); // Get standard attribute "Name" String vendor = (String) mbs.getAttribute(oname, "Name"); // Check if this MXBean contains Sun Microsystems' extension if (mbs.isInstanceOf(oname, "com.sun.management.OperatingSystemMXBean")) { // Get platform-specific attribute "ProcessCpuTime" long cpuTime = (Long) mbs.getAttribute(oname, "ProcessCpuTime"); } } catch (....) { // Catch the exceptions thrown by ObjectName constructor // and MBeanServer methods ... }
スレッドコンテンションおよび CPU 時間の監視
プラットフォーム MXBean の ThreadMXBean では、スレッドコンテンションとスレッドの CPU 時間の監視をサポートします。
Sun HotSpot VM では、スレッドコンテンション監視をサポートします。Java VM でスレッドコンテンション監視がサポートされているかどうかを判断するには、ThreadMXBean.isThreadContentionMonitoringSupported() メソッドを使用します。デフォルト時は、スレッドコンテンション監視が無効になっています。有効にするには、setThreadContentionMonitoringEnabled() メソッドを使用します。
Sun HotSpot VM では、ほとんどのプラットフォームにおいてスレッドの CPU 時間の測定をサポートしています。このインタフェースで発生する CPU 時間は、その単位の精度がナノ秒となっていますが、ここではこれほどの精度は要求されません。
Java VM がどのスレッドの CPU 時間の測定もサポートしていることを判断するには、isThreadCpuTimeSupported() メソッドを使用します。Java VM で現スレッドの CPU 時間を測定できるかどうかを判断するには、isCurrentThreadCpuTimeSupported() を使用します。スレッドの CPU 時間の測定をサポートする Java VM では、現スレッドの CPU 時間もサポートします。
Java VM では、スレッドの CPU 時間の測定を無効にすることができます。スレッドの CPU 時間の測定を有効にするには、isThreadCpuTimeEnabled() メソッドを使用します。スレッドの CPU 時間の測定を有効/無効にするには、setThreadCpuTimeEnabled() メソッドを使用します。
オペレーティングシステムの管理
プラットフォーム MXBean の OperatingSystem では、次に示すような特定のオペレーティングシステムのリソース情報にアクセスすることができます。
-
プロセス CPU 時間。
-
物理メモリーの総容量と空き容量。
-
仮想メモリーの確定容量 (プロセスの実行に使用可能な仮想メモリーの容量)。
-
スワップ空間の総容量および空き容量。
-
オープンファイル記述子の数 (UNIX プラットフォームのみ)。
JConsole の「MBean」タブでオペレーティングシステムの MXBean を選択すると、そのプラットフォームの拡張機能を含むすべての属性と動作が表示されます。数値属性の値フィールドをダブルクリックすることにより、この数値属性の長期にわたる値の変化を監視できます。
ロギングの管理
Java SE プラットフォームでは、ロギングを目的とした特殊な MXBean (LoggingMXBean インタフェース) を提供します。
LoggingMXBean では、次のタスクを実行できます。
-
指定したロガーと関連付けられているログレベルの名前を取得。
-
現在登録済みのロガーのリストを取得。
-
指定したロガーの親の名前を取得。
-
指定の新しいレベルに指定のロガーを設定。
LoggingMXBean の一意の ObjectName は、java.util.logging:type=Logging です。このオブジェクト名は、LogManager.LOGGING_MXBEAN_NAME に保存されています。
LogManager.getLoggingMXBean() の呼び出しにより取得が可能な単一のグローバルインスタンス LoggingMXBean が用意されています。
MXBean の Logging では、ロガー名のリストを説明する LoggerNames 属性を定義します。使用中のアプリケーションでロガーのリストを検出するには、「MBean」タブで java.util.logging ドメインのロギング MXBean を選択し、LoggerNames 属性の値フィールドをダブルクリックします。ロギング MXBean では、次の 2 種類の処理もサポートしています。
-
getLoggerLevel: 既定のロガーのログレベルを返します。
-
setLoggerLevel: 既定ロガーのログレベルを新たなレベルに設定します。
これらの処理では、ロガー名を第 1 パラメータとして取得します。ロガーのログレベルを変更するには、ロガー名を第 1 パラメータに入力し、そのレベルの名前を setLoggerLevel 処理の第 2 パラメータに設定します。
ローメモリーの検出
メモリーの使用は、メモリーシステムの重要な要素となります。これは、次の問題の可能性を示唆しています。
-
アプリケーションの過度のメモリー消費。
-
自動メモリー管理システムへの過度の負荷。
-
潜在的なメモリーリーク。
ローメモリー状態の検出に使用可能なメモリーのしきい値には、使用量しきい値とコレクション使用量しきい値の 2 種類が用意されています。ポーリングまたはしきい値通知でこれらのしきい値のいずれかを使用して、ローメモリー状態を検出できます。これらの概念についてはすべて次のセクションで説明します。
メモリーしきい値
メモリープールには、使用量しきい値およびコレクション使用量しきい値の 2 種類のメモリーしきい値を設定することができます。これらのしきい値のどちらか一方は、特定のメモリープールでサポートされない可能性があります。使用量しきい値とコレクション使用量しきい値の値は、JConsole の「MBean」タブで設定できます。
使用量しきい値
使用量しきい値とは、管理可能な、一部のメモリープールの属性を意味します。オーバーヘッドを低く抑えた状態でメモリーの使用状況を監視することができます。しきい値を正の値に設定すると、メモリープールは使用量しきい値をチェックすることができます。使用量しきい値に 0 を設定すると、この使用量しきい値のチェック機能が無効となります。デフォルト値は、Java VM で用意されています。
Java VM は、メモリープールにおいてほぼ適切なタイミングで (一般的にガベージコレクションが発生している間に)、この使用量しきい値のチェック機能を実行します。各メモリープールは、使用量がしきい値を超える場合に常にその使用量しきい値の数を増加します。
一部のメモリープールには、使用量しきい値が適さないことから、isUsageThresholdSupported() メソッドを使用してメモリープールが使用量しきい値をサポートしているかどうかを確認します。たとえば、世代別ガベージコレクタ (HotSpot VM の機能の 1 つ、第 3 章「JConsole の使用」の「ガベージコレクション」を参照) においては、Eden メモリープールからほとんどのオブジェクトが若い世代の方に割り当てられます。なお、Eden プールはその容量がいっぱいになるように設計されています。Eden メモリープールのガベージコレクトにより、そのメモリー領域のほとんどが解放されます。これは、Eden メモリープールに含まれるオブジェクトのほとんどは、ガベージコレクション時にはアクセス不可能な短命なオブジェクトだと予想されるからです。このため、Eden メモリープールは、使用量しきい値をサポートするのに適していません。
コレクション使用量しきい値
コレクション使用量しきい値とは、ガベージコレクトされた一部のメモリープールの属性を意味し、この属性は管理できるようになっています。Java VM でメモリープールのガベージコレクトを行うと、そのメモリープール中の一部のメモリーはそれ以後も引き続き使用中の状態のままとなります。コレクション使用量しきい値により、このメモリーの値を設定することができます。プールでコレクション使用量しきい値がサポートされているかどうかを判断するには、MemoryPoolMXBean の isCollectionUsageThresholdSupported() メソッドを使用します。
Java VM は、ガベージコレクションを行うときに、メモリープールにおけるコレクション使用量しきい値をチェックする場合があります。なお、このチェック機能を有効にするには、このしきい値に正の値を設定します。チェック機能を無効にする場合は、この値に 0 (デフォルト値) を設定します。
使用量しきい値、とコレクション使用量しきい値は、いずれも「JConsole」の「MBean」タブで設定することができます。
Memory MXBean
プラットフォーム MemoryMXBean を使用してメモリーのさまざまなしきい値を管理することができます。MemoryMXBean では、次の 4 つの属性を定義します。
-
HeapMemoryUsage: 現在のヒープメモリーの使用状況を示す読み取り専用の属性です。
-
NonHeapMemoryUsage: 非ヒープメモリーの使用状況を示す読み取り専用の属性です。
-
ObjectPendingFinalizationCount: ファイナライズを保留しているオブジェクトの数を示す読み取り専用の属性です。
-
Verbose: ガベージコレクション (GC) の冗長トレース設定を示すブール型の属性です。この値は動的に設定することができます。GC の冗長トレースは、Java VM を開始する際に指定した位置に表示されます。Hotspot VM の GC 冗長出力におけるデフォルトの位置は、stdout となります。
メモリー MXBean では、明示的にガベージコレクションを要求できるよう、処理 gc を 1 つサポートしています。
メモリー MXBean のインタフェースに関する詳細は、java.lang.management.MemoryMXBean 仕様に定義されています。
Memory Pool MXBean
プラットフォーム MXBean の MemoryPoolMXBean では、メモリーのしきい値を管理するために一連の処理を定義します。
-
getUsageThreshold()
-
setUsageThreshold(long threshold)
-
isUsageThresholdExceeded()
-
isUsageThresholdSupported()
-
getCollectionUsageThreshold()
-
setCollectionUsageThreshold(long threshold)
-
isCollectionUsageThresholdSupported()
-
isCollectionUsageThresholdExceeded()
各メモリープールには、ローメモリーの検出をサポートする上で、使用量しきい値およびコレクション使用量しきい値の 2 種類のメモリーしきい値が用意されています。これらのしきい値のどちらか一方は、特定のメモリープールでサポートされない可能性があります。詳しくは、MemoryPoolMXBean クラスの API リファレンスドキュメントを参照してください。
ポーリング
アプリケーションでは、すべてのメモリープール用の getUsage() メソッド、または使用量しきい値をサポートするメモリープール用の isUsageThresholdExceeded() メソッドを呼び出すことにより、アプリケーション自体のメモリー使用量を継続的に監視できます。
例 4-6 では、タスクの配置と処理のために設けられた専用のスレッドが使用されています。各間隔では、メモリーの使用量に基づき新しいタスクの受け取りや処理を行うかどうかを判断します。メモリーの使用量がその使用量しきい値を上回る場合、そのメモリーの使用量がしきい値を下回るまで別の VM に未処理のタスクを再配置し、新しいタスクを受け取らないようにします。
例 4-6 ポーリングの使用pool.setUsageThreshold(myThreshold); .... boolean lowMemory = false; while (true) { if (pool.isUsageThresholdExceeded()) { lowMemory = true; redistributeTasks(); // redistribute tasks to other VMs stopReceivingTasks(); // stop receiving new tasks } else { if (lowMemory) { // resume receiving tasks lowMemory = false; resumeReceivingTasks(); } // processing outstanding task ... } // sleep for sometime try { Thread.sleep(sometime); } catch (InterruptedException e) { ... } }
例 4-6 では、メモリーの使用量がその使用量しきい値を一時的に下回る場合と、2 つの繰り返し間においてメモリーの使用量がしきい値を上回ったままの状態である場合の区別がありません。getUsageThresholdCount() から返される使用量しきい値の数を使用して、メモリーの使用量が、2 つのポーリング間でしきい値を下回る値を返すかどうかを判断します。
代わりにコレクション使用量しきい値をテストするには、前述と同じ方法で isCollectionUsageThresholdSupported()、isCollectionThresholdExceeded()、および getCollectionUsageThreshold() メソッドを使用します。
しきい値通知
MemoryMXBean でメモリープールの使用量がそのしきい値に到達しているか、上回っていることが検出されたときは、使用量しきい値超過通知が発行されます。使用量がしきい値を下回り、ふたたび上回るまで、MemoryMXBean は、別の使用量しきい値超過通知を発行しません。同様に、ガベージコレクション後のメモリーの使用量が、コレクション使用量しきい値を上回る場合、MemoryMXBean では、コレクション使用量しきい値超過通知を発行します。
例 4-7 は、例 4-6 と同じロジックを実装していますが、ローメモリー状態を検出するために使用量しきい値通知を使用します。通知を受け取ると、リスナーは、未処理タスクの再配置、新タスクの受け入れ拒否、新タスクの再受け入れなどのアクションを実行するために別のスレッドの通知を行います。
一般に、以後の通知の遅れを回避するために、処理量を最小限にする handleNotification メソッドを設計する必要があります。時間を浪費するアクションは別のスレッドにおいて実行するようにしてください。複数のスレッドで通知リスナーを同時に呼び出すことができるため、リスナーは、タスクが正常に実行されるようにそのタスクを同期させる必要があります。
例 4-7 しきい値通知の使用class MyListener implements javax.management.NotificationListener { public void handleNotification(Notification notification, Object handback) { String notifType = notification.getType(); if (notifType.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) { // potential low memory, redistribute tasks to other VMs & stop receiving new tasks. lowMemory = true; notifyAnotherThread(lowMemory); } } } // Register MyListener with MemoryMXBean MemoryMXBean mbean = ManagementFactory.getMemoryMXBean(); NotificationEmitter emitter = (NotificationEmitter) mbean; MyListener listener = new MyListener(); emitter.addNotificationListener(listener, null, null);
このメモリープールで使用量しきい値をサポートするものと仮定する場合、しきい値 (値を上回るとアプリケーションで新タスクを受け入れなくなるしきい値) をある値 (バイト数表記) に設定することができます。
pool.setUsageThreshold(myThreshold);
このあと、使用量しきい値の検出が有効となり、MyListener で通知が処理されます。