4 プラットフォームMBeanサーバーとプラットフォームMXBeanの使用
このトピックでは、Java Platform, Standard Edition (Java SE)の一部として提供されるMBeanサーバーとMXBeanについて説明します。MBeanサーバーとMXBeanは、モニタリングおよび管理の目的で使用できます。Java Management Extensions (JMX)テクノロジのMBeanとMBeanサーバーについては、Java SEモニタリングおよび管理の概要に概要が記されています。Java Platform, Standard Edition Java Management ExtensionsガイドのJMXテクノロジの概要を参照してください。
プラットフォームMBeanサーバーの使用
MBeanサーバーは、管理アプリケーションからMBeanにアクセスできるようにするMBeanのリポジトリです。アプリケーションはMBeanに直接アクセスしませんが、その一意のObjectNameクラスを使用して、MBeanサーバー経由でMBeanにアクセスします。MBeanサーバーには、インタフェースjavax.management.MBeanServerが実装されています。
                  
プラットフォームMBeanサーバーとは、Java SE 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機能の一部をカプセル化します。プラットフォームで提供されるMXBeanの完全なリストは、表1-1に示されています。
管理アプリケーションでは、次の3種類の方法で、プラットフォームMXBeanにアクセスできます。
- 
                        ManagementFactoryクラスからの直接アクセス
- 
                        MXBeanプロキシからの直接アクセス 
- 
                        MBeanServerConnectionクラスからの間接アクセス
ManagementFactoryクラスを使用したプラットフォームMXBeanへのアクセス
アプリケーションは、自身のJava VMと同一Java VMで稼働するプラットフォームMXBeanのメソッドに対して直接呼出しを行うことができます。直接呼出しには、ManagementFactoryクラスのstaticメソッドを使用できます。ManagementFactoryには、getClassLoadingMXBean()、getGarbageCollectorMXBeans()、getRuntimeMXBean()などの様々なプラットフォームMXBeanにそれぞれ対応するaccessorメソッドが用意されています。プラットフォーム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()メソッドを使用して、MBeanのObjectNameと属性名をパラメータとして指定することで、プラットフォームMXBeanの属性を取得します。
                     
たとえば、例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
  ...
}
Oracle JDKのプラットフォーム拡張機能の使用
Java VMでは、プラットフォームに特化した測定に必要なインタフェースと管理操作を定義することにより管理インタフェースを拡張できます。ManagementFactoryクラスのstaticファクトリ・メソッドは、プラットフォーム拡張機能を備えたMBeanを返します。
                  
com.sun.managementパッケージには、Oracle JDKのプラットフォーム拡張機能が含まれています。次の各セクションでは、Oracle JDKのOperatingSystemMXBeanインタフェースの実装からプラットフォーム固有の属性にアクセスする方法の例について説明します。
                  
MXBeanの各種属性に対する直接アクセス
例4-4では、Oracle JDKの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 Oracle JDK-specific attribute Process CPU time
long cpuTime = mxbean.getProcessCpuTime();
MBeanServerConnectionを使用したMXBean属性へのアクセス
例4-5では、MBeanServerConnectionクラスを使用して、Oracle JDKのMXBeanインタフェースの1つにアクセスします。
                     
例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 Oracle JDK's 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時間のモニタリングをサポートします。
                  
Oracle JDKのHotSpot VMでは、スレッドの競合のモニタリングをサポートしています。Java VMでスレッドの競合のモニタリングがサポートされているかどうかを判断するには、ThreadMXBean.isThreadContentionMonitoringSupported()メソッドを使用します。デフォルト時は、スレッドの競合のモニタリングが無効になっています。有効にするには、setThreadContentionMonitoringEnabled()メソッドを使用します。
                  
Oracle JDKの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時間 
- 
                        物理メモリーの総容量と空き容量 
- 
                        仮想メモリーの確定容量(プロセスの実行に使用可能な仮想メモリーの容量) 
- 
                        スワップ空間の総容量および空き容量 
- 
                        開いているファイル記述子の数(Solaris、LinuxまたはmacOSプラットフォームのみ)。 
JConsoleの「MBean」タブでオペレーティング・システムのMXBeanを選択すると、そのプラットフォームの拡張機能を含むすべての属性と動作が表示されます。数値属性の値フィールドをダブルクリックすることにより、この数値属性の長期にわたる値の変化をモニターできます。
ロギングの管理
Java SEプラットフォームでは、ロギングを目的とした特殊なMXBean (LoggingMXBeanインタフェース)を提供します。
                  
LoggingMXBeanインタフェースでは、次のタスクを実行できます。
                  
- 
                        指定したロガーと関連付けられているログ・レベルの名前を取得 
- 
                        現在登録済みのロガーのリストを取得 
- 
                        指定したロガーの親の名前を取得 
- 
                        指定の新しいレベルに指定のロガーを設定 
LoggingMXBeanの一意のObjectNameは、java.util.logging:type=Loggingです。このオブジェクト名は、LogManager.LOGGING_MXBEAN_NAMEに格納されます。
                  
LogManager.getLoggingMXBean()の呼出しにより取得が可能な、LoggingMXBeanインタフェースの単一のグローバル・インスタンスが用意されています。
                  
LoggingMXBeanインタフェースは、ロガー名のリストを示すLoggerNames属性を定義します。使用中のアプリケーションでロガーのリストを検出するには、「MBean」タブでjava.util.loggingドメインのLogging MXBeanインタフェースを選択し、LoggerNames属性の値フィールドをダブルクリックします。 
                  
Logging MXBeanインタフェースでは、次の2つの操作もサポートされます。
                  
- 
                        getLoggerLevel: 既定のロガーのログ・レベルを返します
- 
                        setLoggerLevel: 既定ロガーのログ・レベルを新たなレベルに設定します
これらの処理では、ロガー名を第1パラメータとして取得します。ロガーのレベルを変更するには、ロガー名を第1パラメータに入力し、そのレベルの名前をsetLoggerLevel操作の第2パラメータに設定します。
                  
ロー・メモリーの検出
メモリーの使用は、メモリー・システムの重要な要素となります。これは、次の問題の可能性を示唆しています。
- 
                        アプリケーションの過度のメモリー消費 
- 
                        自動メモリー管理システムへの過度の負荷 
- 
                        潜在的なメモリー・リーク 
ロー・メモリー状態の検出に使用可能なメモリーのしきい値には、使用量しきい値とコレクション使用量しきい値の2種類が用意されています。ポーリングまたはしきい値通知でこれらのしきい値のいずれかを使用して、ロー・メモリー状態を検出できます。
メモリーしきい値
メモリー・プールには、使用量しきい値およびコレクション使用量しきい値の2種類のメモリーしきい値を設定することができます。これらのしきい値のどちらか一方は、特定のメモリー・プールでサポートされない可能性があります。使用量しきい値とコレクション使用量しきい値の値は、JConsoleの「MBean」タブで設定できます。
使用量しきい値
使用量しきい値とは、管理可能な、一部のメモリー・プールの属性を意味します。オーバーヘッドを低く抑えた状態でメモリーの使用状況をモニターすることができます。しきい値を正の値に設定すると、メモリー・プールは使用量しきい値をチェックすることができます。使用量しきい値に0を設定すると、この使用量しきい値のチェック機能が無効となります。デフォルト値は、Java VMで用意されています。
Java VMは、メモリー・プールにおいてほぼ適切なタイミングで(一般的にガベージ・コレクションが発生している間に)、この使用量しきい値のチェック機能を実行します。各メモリー・プールは、使用量がしきい値を超える場合に常にその使用量しきい値の数を増加します。
一部のメモリー・プールにとって使用量しきい値は適当ではないため、isUsageThresholdSupported()メソッドを使用してメモリー・プールが使用量しきい値をサポートしているかどうかを確認します。たとえば、世代別ガベージ・コレクタ(HotSpot VMのものなど。ガベージ・コレクションを参照)では、Edenメモリー・プールからほとんどのオブジェクトが若い世代の方に割り当てられます。なお、Edenプールはその容量がいっぱいになるように設計されています。Edenメモリー・プールのガベージ・コレクションにより、そのメモリー領域のほとんどが解放されます。これは、Edenメモリー・プールに含まれるオブジェクトのほとんどが、ガベージ・コレクション時にはアクセス不可能な短命なオブジェクトであると予想されるためです。このため、Edenメモリー・プールは、使用量しきい値をサポートするのに適していません。
                        
コレクション使用量しきい値
コレクション使用量しきい値とは、ガベージ・コレクトされた一部のメモリー・プールの属性を意味し、この属性は管理できるようになっています。Java VMでメモリー・プールのガベージ・コレクトを行うと、そのメモリー・プール中の一部のメモリーはそれ以後も引き続き使用中の状態のままとなります。コレクション使用量しきい値により、このメモリーの値を設定することができます。プールでコレクション使用量しきい値がサポートされているかどうかを判断するには、MemoryPoolMXBeanインタフェースのisCollectionUsageThresholdSupported()メソッドを使用します。
                        
Java VMは、ガベージ・コレクションを行うときに、メモリー・プールにおけるコレクション使用量しきい値をチェックする場合があります。なお、このチェック機能を有効にするには、このしきい値に正の値を設定します。チェック機能を無効にする場合は、この値に0 (デフォルト値)を設定します。
使用量しきい値、とコレクション使用量しきい値は、いずれも「JConsole」の「MBean」タブで設定することができます。
メモリーMXBean
プラットフォームMemoryMXBeanを使用してメモリーの様々なしきい値を管理することができます。MemoryMXBeanでは、次の4つの属性を定義します。
                        
- 
                              HeapMemoryUsage: 現在のヒープ・メモリーの使用状況を示す読取り専用の属性です。
- 
                              NonHeapMemoryUsage: 非ヒープ・メモリーの使用状況を示す読取り専用の属性です。
- 
                              ObjectPendingFinalizationCount: ファイナライズを保留しているオブジェクトの数を示す読取り専用の属性です。
- 
                              Verbose: ガベージ・コレクション(GC)の冗長トレース設定を示すブール型の属性です。この値は動的に設定することができます。GCの冗長トレースは、Java VMを開始する際に指定した位置に表示されます。Hotspot VMのGC冗長出力におけるデフォルトの位置は、stdoutとなります。
メモリーMXBeanでは、明示的にガベージ・コレクションを要求できるよう、処理gcを1つサポートしています。
                        
メモリーMXBeanのインタフェースに関する詳細は、java.lang.management.MemoryMXBean仕様に定義されています。
                        
メモリー・プール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つのポーリング間でメモリーの使用量がしきい値を下回る値を返すかどうかを判断します。
                     
かわりにコレクション使用量しきい値をテストするには、例4-6と同じ方法で、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クラスによって通知が処理されます。