MBean サーバは、MBean に管理アプリケーションからアクセスできるようにする MBean のリポジトリです。アプリケーションは、 MBean に直接はアクセスしませんが、MBean サーバを通して、一意の ObjectName
でアクセスします。MBean サーバは、インタフェース javax.management.MBeanServer
を実装します。
J2SE 5.0 では、JVM に組み込みの MBean サーバであるプラットフォーム MBean サーバを導入しており、これらは JVM で動作するすべての管理対象コンポーネントで共有されています。メソッド ManagementFactory.getPlatformMBeanServer()
でプラットフォーム MBean サーバにアクセスします。もちろん、MBeanServerFactory
クラスを使って、独自の MBean サーバを作成することもできます。しかし、通常は 2 つ以上の MBean サーバを作成する必要はなく、プラットフォーム MBean サーバを使用することをお勧めします。
プラットフォーム MBean (MXBean) は、以前から導入されていました。管理アプリケーションは、以下の 3 つの異なる方法でプラットフォーム MBean にアクセスできます。
アプリケーションは、同一の Java 仮想マシン上で実行されるプラットフォーム Mbean のメソッドを呼び出すことができます。そのためには、ManagementFactory
クラスの静的メソッドを使用します。ManagementFactory
には、getClassLoadingMXBean()
、getGarbageCollectorMXBeans()
、getRuntimeMXBean()
などの各 プラットフォーム MBean のアクセス用メソッドがあります。2 つ以上のプラットフォーム MBean がある場合、このメソッドはプラットフォーム MBean の List
を返します。
たとえば、次のコードは、ManagementFactory
の静的メソッドを使用して、プラットフォーム MBean RuntimeMXBean
を取得し、次にプラットフォーム MBean からベンダー名を取得します。
RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();
String vendor = mxbean.getVmVendor();
アプリケーションは、MXBean プロキシを通して、プラットフォーム MBean を呼び出すことができます。そのためには、静的メソッド ManagementFactory.newPlatformMXBeanProxy()
を呼び出すことによって、特定の MBeanServer へのメソッド呼び出しを転送する MXBean プロキシインスタンスを構築します。アプリケーションは通常、別の JVM のプラットフォーム MBean にリモートからアクセスするためにプロキシを構築します。
たとえば、次の例は前の例と同じ操作を実行しますが、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();
アプリケーションは、実行中の JVM の プラットフォーム MBeanServer に接続する MBeanServerConnection
を通して、間接的にプラットフォーム MBean メソッドを呼び出します。MBeanServerConnection
の getAttribute()
メソッドを使用して、プラットフォーム MBean の属性を取得し、MBean の ObjectName
と属性名をパラメータとして提供します。
たとえば、次のコードは前の 2 つの例と同じ操作を実行しますが、MBeanServerConnection
による間接的な呼び出しを使用しています。
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
...
}
JVM の実装は、プラットフォーム固有のメトリックスと管理操作のインタフェースを定義することによって、管理インタフェースを拡張できます。ManagementFactory クラス内の静的ファクトリメソッドは、プラットフォーム拡張により MBean を返します。
com.sun.management
パッケージには、Sun Microsystems のプラットフォーム拡張が含まれています。以下の例は、Sun による RuntimeMXBean の実装からプラットフォーム固有の属性にアクセスする方法を示しています。
次の例は、Sun 固有の 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 経由での Sun 固有の MXBean インタフェースへのアクセスを示しています。
MBeanServerConnection mbs;
// Connect to a running JVM (or itself) and get MBeanServerConnection
// that has the JVM 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 "OperatingSystem"
String vendor = (String) mbs.getAttribute(oname, "OperatingSystem");
// Check if this MXBean contains Sun's extension
if (mbs.isInstanceOf(oname, "com.sun.management.RuntimeMXBean")) {
// Get platform-specific attribute "ProcessCpuTime"
BarType bar = (String) mbs.getAttribute(oname, "ProcessCpuTime");
}
} catch (....) {
// Catch the exceptions thrown by ObjectName constructor
// and MBeanServer methods
...
}
ThreadMXBean
プラットフォーム MBean は、スレッドの競合およびスレッド CPU 時間の監視をサポートしています。
Sun HotSpot JVM は、スレッドの競合の監視をサポートしています。ThreadMXBean.isThreadContentionMonitoringSupported()
メソッドを使用して、JVM がスレッドの競合の監視をサポートしているかどうかを調べます。スレッドの競合の監視はデフォルトでは無効になっています。setThreadContentionMonitoringEnabled()
メソッドを使用して、これを有効にします。
Sun HotSpot JVM は、ほとんどのプラットフォーム上でスレッドの CPU 時間の測定をサポートしています。このインタフェースによって提供される CPU 時間にはナノ秒の精度がありますが、必ずしもナノ秒の正確さは必要ありません。
isThreadCpuTimeSupported()
を使用して、JVM が任意のスレッドの CPU 時間の測定をサポートしているかどうかを調べ、isCurrentThreadCpuTimeSupported()
を使用して、JVM が現在のスレッドの CPU 時間の測定をサポートしているかどうかを調べます。任意のスレッドの CPU 時間の測定をサポートする Java 仮想マシンの実装は、現在のスレッドの CPU 時間の測定もサポートします。
JVM は、スレッドの CPU 時間の測定を無効にできます。isThreadCpuTimeEnabled()
メソッドを使用して、スレッドの CPU 時間の測定が有効かどうかを調べます。setThreadCpuTimeEnabled()
メソッドを使用して、このスレッドの CPU 時間の測定を有効/無効にします。
メモリの使用は、メモリシステムの重要な属性です。次の情報を表示できます。
以下に説明するようにメモリ不足状態の検出に使用できる 2 種類のメモリしきい値 (使用率しきい値とコレクション使用率しきい値) があります。これらのしきい値のいずれかをポーリングまたはしきい値通知と共に使用してメモリ不足状態を検出できます。
注:メモリの監視は、負荷分散または作業負荷分散を目的としています。たとえば、アプリケーションはメモリ使用率が特定のしきい値を超過すると、新しい作業負荷の受信を停止します。メモリ監視は、アプリケーションのメモリ不足状態の検出および復旧を目的としたものではありません。
詳細は、MemoryPoolMXBean の API リファレンスを参照してください。
メモリプールは、使用率しきい値とコレクション使用率しきい値の 2 種類のメモリしきい値を持つことがあります。これらのしきい値のいずれも特定のメモリプールによってサポートされていない場合があります。
使用率しきい値は、一部のメモリプールの管理可能な属性です。少ないオーバーヘッドでメモリの使用を監視できます。しきい値を正の値に設定すると、メモリプールで使用率のしきい値のチェックを有効にできます。使用率のしきい値をゼロに設定すると、使用率のしきい値のチェックを無効にできます。デフォルト値は、JVM によって提供されます。
JVM はメモリプールで、適切な時間 (通常は、ガベージコレクション (GC) 中) に使用率のしきい値のチェックを実行します。各メモリプールは、使用率がしきい値を超えるたびに使用率のしきい値の数を増やします。
メモリプールによっては使用率のしきい値が適切でない場合があるため、isUsageThresholdSupported()
メソッドを使用して、メモリプールが使用率のしきい値をサポートするかどうかを調べます。たとえば、世代別のガベージコレクタ (HotSpot VM など) では、ほとんどのオブジェクトが「eden」メモリプールからの若い世代に割り当てられています。eden プールはいっぱいになるように設計されています。ガベージコレクション時に収集されなかった、ほとんどが短命なオブジェクトが含まれているため、メモリプールをガベージコレクトすれば、メモリ容量のほとんどが空きます。従って、eden メモリプールで使用率のしきい値をサポートするのは適切ではありません。
コレクション使用率しきい値は、一部のガベージコレクトされるメモリプールの管理可能な属性です。JVM がメモリプール上でガベージコレクションを実行した後、プール内の一部のメモリはまだ使用されています。コレクション使用率しきい値によりこのメモリの値を設定できます。MemoryPoolMXBean の isCollectionUsageThresholdSupported()
メソッドを使用して、プールがコレクション使用率しきい値をサポートするかどうかを調べます。
JVM は、GC を実行するときにメモリプール上のコレクション使用率しきい値をチェックします。コレクション使用率しきい値を正の値に設定すると、チェックが有効になります。コレクション使用率しきい値をゼロ (デフォルト) に設定すると、チェックが無効になります。
アプリケーションは、すべてのメモリプールに getUsage()
メソッドを呼び出すか、または使用率のしきい値をサポートするメモリプールに isUsageThresholdExceeded()
メソッドを呼び出すことにより、継続的にメモリ使用率を監視できます。
次の例には、タスクの配布と処理専用のスレッドがあります。このスレッドは、インターバルごとに、メモリ使用率に基づいて新規タスクを受け取って処理するかどうかを決めます。メモリ使用率が使用率のしきい値を超えると、スレッドは超過したタスクを他の VM に再配布し、メモリ使用率がしきい値より下になるまで、新たなタスクの受け取りを停止します。
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) {
...
}
}
上の例は、メモリ使用率が一時的に使用率のしきい値を下回る場合とメモリ使用率が 2 回のポーリングの繰り返しの間、使用率のしきい値を上回る場合を区別していません。getUsageThresholdCount()
で返される使用率のしきい値の数を使用して、2 回のポーリングの間で使用率がしきい値を下回るかどうかを調べることができます。
上記の代わりにコレクション使用率しきい値をテストするには、isCollectionUsageThresholdSupported()
、isCollectionThresholdExceeded()
および getCollectionUsageThreshold()
メソッドを同様に使用します。
MemoryMXBean
が、メモリプールが使用率のしきい値に達するか、しきい値を超えたことを検出すると、使用率しきい値超過通知を出します。MemoryMXBean
は、もう一度使用率がしきい値を下回って、次にしきい値を上回るまで、使用率しきい値超過通知を発行しません。同様に、ガベージコレクション後のメモリ使用率がコレクション使用率しきい値を超えると、MemoryMXBean
がコレクション使用率しきい値超過通知を発行します。
下のコード例は、ポーリングの例と同じロジックを実装していますが、使用率しきい値通知を使用して、メモリ不足状態を検出しています。通知を受け取ると、リスナーは別のスレッドに超過タスクの再配布、タスクの受領の停止、タスクの受領の再開などを指示します。
通常、handleNotification メソッドは最小量の作業を実行し、その後の通知の配布が遅延しないように設計します。時間のかかるアクションは、別のスレッドで実行してください。複数のスレッドが同時に通知リスナーを呼び出す可能性があるため、リスナーは実行するタスクを正しく同期化する必要があります。
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 が通知を受け取ります。