在 Java 程序的運行過程中,對 JVM 和系統(tǒng)的監(jiān)測一直是 Java 開發(fā)人員在開發(fā)過程所需要的。一直以來,Java 開發(fā)人員必須通過一些底層的 JVM API,比如 JVMPI 和 JVMTI 等,才能監(jiān)測 Java 程序運行過程中的 JVM 和系統(tǒng)的一系列情況,這種方式一直以來被人所詬病,因為這需要大量的 C 程序和 JNI 調(diào)用,開發(fā)效率十分低下。于是出現(xiàn)了各種不同的專門做資源管理的程序包。為了解決這個問題,Sun 公司也在其 Java SE 5 版本中,正式提出了 Java 管理擴(kuò)展(Java Management Extensions,JMX)用來管理檢測 Java 程序(同時 JMX 也在 J2EE 1.4 中被發(fā)布)。
JMX 的提出,讓 JDK 中開發(fā)自檢測程序成為可能,也提供了大量輕量級的檢測 JVM 和運行中對象/線程的方式,從而提高了 Java 語言自己的管理監(jiān)測能力。
![]() ![]() |
![]()
|
要 了解 JMX,我們就必須對當(dāng)前的 IT 管理系統(tǒng)有一個初步的了解。隨著企業(yè) IT 規(guī)模的不斷增長,IT 資源(IT resource)數(shù)量不斷增加,IT 資源的分布也越來越分散。可以想象,甚至對于一家只有幾百臺 PC 公司的 IT 管理人員來說,分發(fā)一個安全補(bǔ)丁并且保證其在每臺 PC 上的安裝,如果只依賴人工來完成那簡直就是一場噩夢。這樣,IT 管理系統(tǒng)就應(yīng)運而生。
然 而,CPU、網(wǎng)卡、存儲陣列是 IT 資源;OS、MS Office、Oracle database、IBM Websphere 也是 IT 資源。IT 管理系統(tǒng)若要對這些 IT 資源進(jìn)行管理,就必須對這些管理對象有所了解:形形色色的 IT 資源就像是說著不同語言的人:Oralce 數(shù)據(jù)庫表達(dá)內(nèi)存緊張的方式和 Window XP 是絕然不同的, 而 IT 管理系統(tǒng)就像建造通天塔的經(jīng)理,必須精通所有的語言, 這幾乎是一個不可能完成的任務(wù)。難道 IT 管理系統(tǒng)是另外一個通天塔嗎?當(dāng)然不是!其實我們只要給每個 IT 資源配個翻譯就可以了。
圖 1. 管理系統(tǒng)構(gòu)架

上圖分析了管理系統(tǒng)的基本構(gòu)架模式。其中 Agent / SubAgent 起到的就是翻譯的作用:把 IT 資源報告的消息以管理系統(tǒng)能理解的方式傳送出去。
也許讀者有會問,為什么需要 Agent 和 SubAgent 兩層體系呢?這里有兩個現(xiàn)實的原因:
- 管理系統(tǒng)一般是一個中央控制的控制軟件,而 SubAgent 直接監(jiān)控一些資源,往往和這些資源分布在同一物理位置。當(dāng)這些 SubAgent 把狀態(tài)信息傳輸?shù)焦芾硐到y(tǒng)或者傳達(dá)管理系統(tǒng)的控制指令的時候,需要提供一些網(wǎng)絡(luò)傳輸?shù)墓δ堋?/li>
- 管理系統(tǒng)的消息是有一定規(guī)范的,消息的翻譯本身是件復(fù)雜而枯燥的事情。
一般來說,管理系統(tǒng)會將同一物理分布或者功能類似的 SubAgent 分組成一組,由一個共用的 Agent 加以管理。在這個 Agent 里封裝了 1 和 2 的功能。
JMX 既是 Java 管理系統(tǒng)的一個標(biāo)準(zhǔn),一個規(guī)范,也是一個接口,一個框架。圖 2 展示了 JMX 的基本架構(gòu)。
圖 2. JMX 構(gòu)架

和其它的資源系統(tǒng)一樣,JMX 是管理系統(tǒng)和資源之間的一個接口,它定義了管理系統(tǒng)和資源之間交互的標(biāo)準(zhǔn)。javax.management.MBeanServer
實現(xiàn)了 Agent 的功能,以標(biāo)準(zhǔn)的方式給出了管理系統(tǒng)訪問 JMX 框架的接口。而 javax.management.MBeans
實現(xiàn)了 SubAgent 的功能,以標(biāo)準(zhǔn)的方式給出了 JMX 框架訪問資源的接口。而從類庫的層次上看,JMX 包括了核心類庫 java.lang.management
和 javax.management
包。java.lang.management
包提供了基本的 VM 監(jiān)控功能,而 javax.management
包則向用戶提供了擴(kuò)展功能。
![]() ![]() |
![]()
|
JMX
使用了 Java Bean 模式來傳遞信息。一般說來,JMX 使用有名的
MBean,其內(nèi)部包含了數(shù)據(jù)信息,這些信息可能是:應(yīng)用程序配置信息、模塊信息、系統(tǒng)信息、統(tǒng)計信息等。另外,MBean
也可以設(shè)立可讀寫的屬性、直接操作某些函數(shù)甚至啟動 MBean 可發(fā)送的 notification 等。MBean 包括
Standard,MXBean,Dynamic,Model,Open 等幾種分類,其中最簡單是標(biāo)準(zhǔn) MBean 和
MXBean,而我們使用得最多的也是這兩種。MXBean 主要是 java.lang.management
使用較多,將在下一節(jié)中介紹。我們先了解其他一些重要的 MBean 的種類。
標(biāo)準(zhǔn) MBean 是最簡單的一類 MBean,與動態(tài) Bean 不同,它并不實現(xiàn) javax.management
包中的特殊的接口。說它是標(biāo)準(zhǔn) MBean, 是因為其向外部公開其接口的方法和普通的 Java Bean 相同,是通過 lexical,或者說
coding convention 進(jìn)行的。下面我們就用一個例子來展現(xiàn),如何實現(xiàn)一個標(biāo)準(zhǔn) MBean 來監(jiān)控某個服務(wù)器 ServerImpl
狀態(tài)的。ServerImpl 代表了用來演示的某個 Server 的實現(xiàn):
public class ServerImpl {
public final long startTime;
public ServerImpl() {
startTime = System.currentTimeMillis();
}
}
public class ServerMonitor implements ServerMonitorMBean {
private final ServerImpl target;
public ServerMonitor(ServerImpl target){
this.target = target;
}
public long getUpTime(){
return System.currentTimeMillis() - target.startTime;
}
}
這里的 ServerMonitorBean 又是怎么回事呢?MXBean 規(guī)定了標(biāo)準(zhǔn) MBean 也要實現(xiàn)一個接口,所有向外界公開的方法都要在這個接口中聲明。否則,管理系統(tǒng)就不能從中獲得相應(yīng)的信息。此外,該接口的名字也有一定的規(guī)范:即在標(biāo)準(zhǔn) MBean 類名之后加上“MBean”后綴。若 MBean 的類名叫做 MBeansName 的話,對應(yīng)的接口就要叫做 MBeansNameMBean。
對于管理系統(tǒng)來說,這些在 MBean 中公開的方法,最終會被 JMX 轉(zhuǎn)化成屬性(Attribute)、監(jiān)聽(Listener)和調(diào)用(Invoke)的概念。如果讀者對 Java Bean 有一些了解的話,不難看出,public long getUpTime()
對應(yīng)了 Bean 中的一個稱為“upTime”的只讀屬性。
下面我們就看一個模擬管理系統(tǒng)的例子:
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
public class Main {
private static ObjectName objectName ;
private static MBeanServer mBeanServer;
public static void main(String[] args) throws Exception{
init();
manage();
}
private static void init() throws Exception{
ServerImpl serverImpl = new ServerImpl();
ServerMonitor serverMonitor = new ServerMonitor(serverImpl);
mBeanServer = MBeanServerFactory.createMBeanServer();
objectName = new ObjectName("objectName:id=ServerMonitor1");
mBeanServer.registerMBean(serverMonitor,objectName);
}
private static void manage() throws Exception{
Long upTime = (Long) mBeanServer.getAttribute(objectName,
"upTime");
System.out.println(upTime);
}
}
JMX 的核心是 MBServer。Java SE 已經(jīng)提供了一個默認(rèn)實現(xiàn),可以通過 MBServerFactory.createMBeanServer()
獲得。每個資源監(jiān)控者(MBean)一般都會有名稱(ObjectName), 登記在 MBServer 內(nèi)部的一個 Repository
中。注意,這個 ObjectName 對于每一個 MBServer 必須是唯一的,只能對應(yīng)于一個 MBean。(讀者有興趣的話,可以試著再給
mBeanServer 注冊一個同名的 objectName,看看會怎么樣。) 上述例子是在 init()
方法中完成向 MBeanServer 注冊工作的。
在管理過程中,管理系統(tǒng)并不與資源或者 SubAgent 直接打交道,也就是說,這里不會直接引用到 MBean。而是通過 MBeanServer 的 getAttribute
方法取得對應(yīng) MBean 的屬性的。
但 是對于很多已有的 SubAgent 實現(xiàn),其 Coding Convention 并不符合標(biāo)準(zhǔn) MBean 的要求。重構(gòu)所有這些 SubAgent 以符合標(biāo)準(zhǔn) MBean 標(biāo)準(zhǔn)既費力也不實際。JMX 中給出了動態(tài)(Dynamic) MBean 的概念,MBServer 不再依據(jù) Coding Convention 而是直接查詢動態(tài) MBean 給出的元數(shù)據(jù)(meta data)以獲得 MBean 的對外接口。
import javax.management.*;
import java.lang.reflect.*;
public class ServerMonitor implements DynamicMBean {
private final ServerImpl target;
private MBeanInfo mBeanInfo;
public ServerMonitor(ServerImpl target){
this.target = target;
}
//實現(xiàn)獲取被管理的 ServerImpl 的 upTime
public long upTime(){
return System.currentTimeMillis() - target.startTime;
}
//javax.management.MBeanServer 會通過查詢 getAttribute("Uptime") 獲得 "Uptime" 屬性值
public Object getAttribute(String attribute) throws AttributeNotFoundException,
MBeanException, ReflectionException {
if(attribute.equals("UpTime")){
return upTime();
}
return null;
}
//給出 ServerMonitor 的元信息。
public MBeanInfo getMBeanInfo() {
if (mBeanInfo == null) {
try {
Class cls = this.getClass();
//用反射獲得 "upTime" 屬性的讀方法
Method readMethod = cls.getMethod("upTime", new Class[0]);
//用反射獲得構(gòu)造方法
Constructor constructor = cls.getConstructor(new Class[]
{ServerImpl.class});
//關(guān)于 "upTime" 屬性的元信息:名稱為 UpTime,只讀屬性(沒有寫方法)。
MBeanAttributeInfo upTimeMBeanAttributeInfo = new MBeanAttributeInfo(
"UpTime", "The time span since server start",
readMethod, null);
//關(guān)于構(gòu)造函數(shù)的元信息
MBeanConstructorInfo mBeanConstructorInfo = new MBeanConstructorInfo(
"Constructor for ServerMonitor", constructor);
//ServerMonitor 的元信息,為了簡單起見,在這個例子里,
//沒有提供 invocation 以及 listener 方面的元信息
mBeanInfo = new MBeanInfo(cls.getName(),
"Monitor that controls the server",
new MBeanAttributeInfo[] { upTimeMBeanAttributeInfo },
new MBeanConstructorInfo[] { mBeanConstructorInfo },
null, null);
} catch (Exception e) {
throw new Error(e);
}
}
return mBeanInfo;
}
public AttributeList getAttributes(String[] arg0) {
return null;
}
public Object invoke(String arg0, Object[] arg1, String[] arg2)
throws MBeanException,
ReflectionException {
return null;
}
public void setAttribute(Attribute arg0) throws AttributeNotFoundException,
InvalidAttributeValueException, MBeanException, ReflectionException {
return;
}
public AttributeList setAttributes(AttributeList arg0) {
return null;
}
}
另外還有兩類 MBean:Open MBean 和 Model MBean。實際上它們也都是動態(tài) MBean。
Open MBean 與其它動態(tài) MBean 的唯一區(qū)別在于,前者對其公開接口的參數(shù)和返回值有所限制 —— 只能是基本類型或者 javax.management.openmbean
包內(nèi)的 ArrayType、CompositeType、TarbularType 等類型。這主要是考慮到管理系統(tǒng)的分布,很可能遠(yuǎn)端管理系統(tǒng)甚至 MBServer 層都不具有 MBean 接口中特殊的類。
然
而,普通的動態(tài) Bean 通常缺乏一些管理系統(tǒng)所需要的支持:比如持久化 MBean
的狀態(tài)、日志記錄、緩存等等。如果讓用戶去一一實現(xiàn)這些功能確實是件枯燥無聊的工作。為了減輕用戶的負(fù)擔(dān),JMX 提供商都會提供不同的
ModelBean 實現(xiàn)。其中有一個接口是 Java 規(guī)范中規(guī)定所有廠商必須實現(xiàn)的:javax.management.modelmbean.RequiredModelBean
。
通過配置 Descriptor 信息,我們可以定制這個 Model Bean, 指定哪些 MBean
狀態(tài)需要記入日志、如何記錄以及是否緩存某些屬性、緩存多久等等。這里,我們以 RequiredModelBean 為例討論
ModelBean。比如,我們先來看一個例子,首先是 server 端:
public class Server {
private long startTime;
public Server() { }
public int start(){
startTime = System.currentTimeMillis();
return 0;
}
public long getUpTime(){
return System.currentTimeMillis() - startTime;
}
}
然后我們對它的監(jiān)測如下:
import javax.management.*;
import javax.management.modelmbean.*;
public class Main {
public static void main(String[] args) throws Exception{
MBeanServer mBeanServer = MBeanServerFactory.createMBeanServer();
RequiredModelMBean serverMBean =
(RequiredModelMBean) mBeanServer.instantiate(
"javax.management.modelmbean.RequiredModelMBean");
ObjectName serverMBeanName =
new ObjectName("server: id=Server");
serverMBean.setModelMBeanInfo(getModelMBeanInfoForServer(serverMBeanName));
Server server = new Server();
serverMBean.setManagedResource(server, "ObjectReference");
ObjectInstance registeredServerMBean =
mBeanServer.registerMBean((Object) serverMBean, serverMBeanName);
serverMBean.invoke("start",null, null);
Thread.sleep(1000);
System.out.println(serverMBean.getAttribute("upTime"));
Thread.sleep(5000);
System.out.println(serverMBean.getAttribute("upTime"));
}
private static ModelMBeanInfo getModelMBeanInfoForServer(ObjectName objectName)
throws Exception{
ModelMBeanAttributeInfo[] serverAttributes =
new ModelMBeanAttributeInfo[1];
Descriptor upTime =
new DescriptorSupport(
new String[] {
"name=upTime",
"descriptorType=attribute",
"displayName=Server upTime",
"getMethod=getUpTime",
});
serverAttributes[0] =
new ModelMBeanAttributeInfo(
"upTime",
"long",
"Server upTime",
true,
false,
false,
upTime);
ModelMBeanOperationInfo[] serverOperations =
new ModelMBeanOperationInfo[2];
Descriptor getUpTimeDesc =
new DescriptorSupport(
new String[] {
"name=getUpTime",
"descriptorType=operation",
"class=modelmbean.Server",
"role=operation"
});
MBeanParameterInfo[] getUpTimeParms = new MBeanParameterInfo[0];
serverOperations[0] = new ModelMBeanOperationInfo("getUpTime",
"get the up time of the server",
getUpTimeParms,
"java.lang.Long",
MBeanOperationInfo.ACTION,
getUpTimeDesc);
Descriptor startDesc =
new DescriptorSupport(
new String[] {
"name=start",
"descriptorType=operation",
"class=modelmbean.Server",
"role=operation"
});
MBeanParameterInfo[] startParms = new MBeanParameterInfo[0];
serverOperations[1] = new ModelMBeanOperationInfo("start",
"start(): start server",
startParms,
"java.lang.Integer",
MBeanOperationInfo.ACTION,
startDesc);
ModelMBeanInfo serverMMBeanInfo =
new ModelMBeanInfoSupport(
"modelmbean.Server",
"ModelMBean for managing an Server",
serverAttributes,
null,
serverOperations,
null);
//Default strategy for the MBean.
Descriptor serverDescription =
new DescriptorSupport(
new String[] {
("name=" + objectName),
"descriptorType=mbean",
("displayName=Server"),
"type=modelmbean.Server",
"log=T",
"logFile=serverMX.log",
"currencyTimeLimit=10" });
serverMMBeanInfo.setMBeanDescriptor(serverDescription);
return serverMMBeanInfo;
}
很明顯,和其它 MBean 類似,使用 Model MBean 的過程也是下面幾步:
- 創(chuàng)建一個 MBServer:mBeanServe
- 獲得管理資源用的 MBean:serverBean
- 給這個 MBean 一個 ObjectName:serverMBeanName
- 將 serverBean 以 serverMBeanName 注冊到 mBeanServer 上去
唯一不同的是,ModelMBean 需要額外兩步:
2.serverMBean.setManagedResource(server, "ObjectReference");
第一步用于提供 serverMBean 的元數(shù)據(jù),主要包括以下兩類
- 類似于普通的動態(tài) MBean,需要 MBean 的 Attribute、Invocation、Notification 的類型/反射信息,諸如返回類型、參數(shù)類型和相關(guān)的 get/set 方法等。這里將不再贅述。
- 關(guān)于緩存、持久化以及日志等的策略。后面我們將介紹一些這方面的信息。
第二步指出了 ServerMBean 管理的對象,也就是說,從元數(shù)據(jù)中得到的 Method 將施加在哪個 Object 上。需要指出的是 setManagedResource(Object o, String type);
中第二個參數(shù)是 Object 類型,可以是 "ObjectReference"、"Handle"、"IOR"、"EJBHandle" 或
"RMIReference"。目前 SE 中的實現(xiàn)只支持 "ObjectReference"。筆者認(rèn)為后面幾種類型是為了將來 JMX
管理對象擴(kuò)展而設(shè)定的,可能將來 Model Bean 不僅可以管理 Plain Java Object(POJO),還可能管理 Native
Resource, 并給諸如 EJB 和 RMI 對象的管理提供更多的特性。
Model Bean
與普通動態(tài) Bean 區(qū)別在于它的元數(shù)據(jù)類型 ModelMBeanInfo 擴(kuò)展了前者的 MBeanInfo,使得
ModelMBeanOperationInfo、ModelMBeanConstructor_Info、
ModelMBeanAttributeInfo 和 ModelMBeanNotificationInfo 都有一個額外的元數(shù)據(jù):javax.management.Descriptor
,
它是用來設(shè)定 Model Bean 策略的。數(shù)據(jù)的存儲是典型的 "key-value" 鍵值對。不同的 Model Bean 實現(xiàn),以及不同的
MBeanFeatureInfo 支持不同的策略特性。下面我們就以 Attribute 為例,看一下 RequiredModelBean
支持的策略。
首先,它最重要的 Descriptor 主要是 name、displayName 和 descriptorType,其中 name 是屬性名稱。"name" 要與對應(yīng) ModelMBeanAttributeInfo 的 name 相同。descriptorType 必須是 "attribute"。
另外,value、default、 legalValues "value" 是用來設(shè)定初始值的,"default" 指當(dāng)不能從 resource 中獲得該屬性時的默認(rèn)返回值,"legalValues" 是一組合法的屬性數(shù)據(jù)。它并不用來保證 setAttribute 的數(shù)據(jù)一致性,而是在 UI 系統(tǒng),如 JConsole 中提示用戶可能的數(shù)據(jù)輸入。
在屬性訪問的 getMethod, setMethod 方法上,事實上所有對屬性的訪問都會被 delegate 給同一 MBeanInfo 中特定的 Operation。 getMethod/setMethod 給出了對應(yīng)的 ModelMBeanOperationInfo 名稱。
還 有一些額外的屬性,比如:persistPolicy, persistPeriod 是代表了持久化策略;currencyTimeLimit, lastUpdatedTimeStamp 緩存策略;iterable 屬性是否必須使用 iterate 來訪問。默認(rèn)為否;protocolMap 定義了與第三方系統(tǒng)有關(guān)的數(shù)據(jù)轉(zhuǎn)換的 data model;visibility 定義了與第三方 UI 系統(tǒng)有關(guān)的 MBean 如何顯示的策略;presentationString 也是定義了與第三方 UI 系統(tǒng)有關(guān)的 MBean 如何顯示策略,比如 "presentation=server.gif"。
事實上,策略特性有兩個層次的作用域:整個 Model Bean 和特定的 MBeanFeature。
Model Bean 的策略描述會被施加到該 Model Bean 的所有 MBeanFeature 上去,除非該 MBeanFeature 重寫了這個策略特性。
在上面的例子里,這一個語句:
給整個 serverMBeanInfo 設(shè)了一個策略描述 serverDescription,其中用 "currencyTimeLimit=10" 指出屬性的緩存時間是 10 秒。所以,在 Main 方法中,兩次 serverMBean.getAttribute("upTime");之間的間隔小于 10 秒就會得到同樣的緩存值。
如果我們不想讓 "upTime" 這個屬性被緩存,我們可以在它的策略描述中加入 "currencyTimeLimit=-1":
new String[] {
"name=upTime",
"descriptorType=attribute",
"displayName=Server upTime",
"getMethod=getUpTime",
"currencyTimeLimit=-1" //不需要緩存
});
Descriptor getUpTimeDesc =
new DescriptorSupport(
new String[] {
"name=getUpTime",
"descriptorType=operation",
"class=modelmbean.Server",
"role=operation"
,"currencyTimeLimit=-1" //不需要緩存
});
getUpTimeDesc 也要改動的原因是 RequiredModelBean 會把獲取 upTime 屬性的工作 delegate 給 getUpTime invocation。只要其中一處使用 MBean 級的緩存策略,就沒法獲得實時 upTime 數(shù)據(jù)了。
![]() ![]() |
![]()
|
JMX 的提出,為 Java 虛擬機(jī)提供了 Java 層上的檢測機(jī)制。J2SE 中,新提出的 java.lang.management
包即是 JMX 在 JDK 的一個應(yīng)用,它提供了大量的有用的接口,通過 MBean 方式,提供了對 Java
虛擬機(jī)和運行時遠(yuǎn)端的監(jiān)控和檢測方式,來幫助用戶來檢測本地或者遠(yuǎn)端的虛擬機(jī)的運行情況。有了 JMX
之后,我們可以設(shè)計一個客戶端,來檢測遠(yuǎn)端一個正在運行的虛擬機(jī)中的線程數(shù)、線程當(dāng)前的 Stack、內(nèi)存管理、GC
所占用的時間、虛擬機(jī)中的對象和當(dāng)前虛擬機(jī)參數(shù)等重要的參數(shù)和運行時信息。JMX
另外的一個重要功能是對配置信息的檢測和再配置。比如,我們可以在遠(yuǎn)端查看和修改當(dāng)前 JVM 的 verbose
參數(shù),以達(dá)到動態(tài)管理的目的。甚至,我們可以在遠(yuǎn)端指揮 JVM 做一次 GC,這在下文中有詳細(xì)介紹。
檢測虛擬機(jī)當(dāng)前的狀態(tài)總是 Java 開放人員所關(guān)心的,也正是因為如此,出現(xiàn)了大量的 profiler 工具來檢測當(dāng)前的虛擬機(jī)狀態(tài)。從 Java SE 5 之后,在 JDK 中,我們有了一些 Java 的虛擬機(jī)檢測 API,即 java.lang.management
包。Management 包里面包括了許多 MXBean 的接口類和 LockInfo、MemoryUsage、MonitorInfo 和
ThreadInfo
等類。從名字可以看出,該包提供了虛擬機(jī)內(nèi)存分配、垃圾收集(GC)情況、操作系統(tǒng)層、線程調(diào)度和共享鎖,甚至編譯情況的檢測機(jī)制。這樣一來,Java
的開發(fā)人員就可以很簡單地為自己做一些輕量級的系統(tǒng)檢測,來確定當(dāng)前程序的各種狀態(tài),以便隨時調(diào)整。
要獲得這些信息,我們首先通過 java.lang.management.ManagementFactory
這個工廠類來獲得一系列的 MXBean。包括:
-
ClassLoadingMXBean
ClassLoadMXBean 包括一些類的裝載信息,比如有多少類已經(jīng)裝載/卸載(unloaded),虛擬機(jī)類裝載的 verbose 選項(即命令行中的 Java –verbose:class 選項)是否打開,還可以幫助用戶打開/關(guān)閉該選項。
-
CompilationMXBean
CompilationMXBean 幫助用戶了解當(dāng)前的編譯器和編譯情況,該 mxbean 提供的信息不多。
-
GarbageCollectorMXBean
相 對于開放人員對 GC 的關(guān)注程度來說,該 mxbean 提供的信息十分有限,僅僅提供了 GC 的次數(shù)和 GC 花費總時間的近似值。但是這個包中還提供了三個的內(nèi)存管理檢測類:MemoryManagerMXBean,MemoryMXBean 和 MemoryPoolMXBean。
-
MemoryManagerMXBean
這個類相對簡單,提供了內(nèi)存管理類和內(nèi)存池(memory pool)的名字信息。
-
MemoryMXBean
這個類提供了整個虛擬機(jī)中內(nèi)存的使用情況,包括 Java 堆(heap)和非 Java 堆所占用的內(nèi)存,提供當(dāng)前等待 finalize 的對象數(shù)量,它甚至可以做 gc(實際上是調(diào)用 System.gc)。
-
MemoryPoolMXBean
該 信息提供了大量的信息。在 JVM 中,可能有幾個內(nèi)存池,因此有對應(yīng)的內(nèi)存池信息,因此,在工廠類中,getMemoryPoolMXBean() 得到是一個 MemoryPoolMXBean 的 list。每一個 MemoryPoolMXBean 都包含了該內(nèi)存池的詳細(xì)信息,如是否可用、當(dāng)前已使用內(nèi)存/最大使用內(nèi)存值、以及設(shè)置最大內(nèi)存值等等。
-
MemoryManagerMXBean
-
OperatingSystemMXBean
該類提供的是操作系統(tǒng)的簡單信息,如構(gòu)架名稱、當(dāng)前 CPU 數(shù)、最近系統(tǒng)負(fù)載等。
-
RuntimeMXBean
運行時信息包括當(dāng)前虛擬機(jī)的名稱、提供商、版本號,以及 classpath、bootclasspath 和系統(tǒng)參數(shù)等等。
-
ThreadMXBean
在 Java 這個多線程的系統(tǒng)中,對線程的監(jiān)控是相當(dāng)重要的。ThreadMXBean 就是起到這個作用。ThreadMXBean 可以提供的信息包括各個線程的各種狀態(tài),CPU 占用情況,以及整個系統(tǒng)中的線程狀況。從 ThreadMXBean 可以得到某一個線程的 ThreadInfo 對象。這個對象中則包含了這個線程的所有信息。
java.lang.management 和虛擬機(jī)的關(guān)系
我 們知道,management 和底層虛擬機(jī)的關(guān)系是非常緊密的。其實,有一些的是直接依靠虛擬機(jī)提供的公開 API 實現(xiàn)的,比如 JVMTI;而另外一些則不然,很大一塊都是由虛擬機(jī)底層提供某些不公開的 API / Native Code 提供的。這樣的設(shè)計方式,保證了 management 包可以提供足夠的信息,并且使這些信息的提供又有足夠的效率;也使 management 包和底層的聯(lián)系非常緊密。
![]() ![]() |
![]()
|
Management 在 Java SE 5 被提出之后,受到了歡迎。在 Java 6 當(dāng)中,這個包提供更多的 API 來更好地提供信息。
OperatingSystemMXBean. getSystemLoadAverage()
Java
程序通常關(guān)注是虛擬機(jī)內(nèi)部的負(fù)載、內(nèi)存等狀況,而不考慮整個系統(tǒng)的狀況。但是很多情況下,Java
程序在運行過程中,整個計算機(jī)系統(tǒng)的系統(tǒng)負(fù)荷情況也會對虛擬機(jī)造成一定的影響。隨著 Java 的發(fā)展,Java
程序已經(jīng)覆蓋了各個行業(yè),這一點也必須得到關(guān)注。在以前,利用 Native 代碼來檢測系統(tǒng)負(fù)載往往是唯一的選擇,但是在 Java 6
當(dāng)中,JDK 自己提供了一個輕量級的系統(tǒng)負(fù)載檢測 API,即 OperatingSystemMXBean.getSystemLoadAverage()
。
當(dāng)然這個 API 事實上僅僅返回一個對前一分鐘系統(tǒng)負(fù)載的簡單的估測。它設(shè)計的主要目標(biāo)是簡單快速地估測當(dāng)前系統(tǒng)負(fù)荷,因此它首先保證了這個 API 的效率是非常高的;也因為如此,這個 API 事實上并不適用于所有的系統(tǒng)。
我 們知道,同步是 Java 語言很重要的一個特性。在 Java SE 中,最主要的同步機(jī)制是依靠 synchronize 關(guān)鍵字對某一個對象加鎖實現(xiàn)的;在 Java SE 5 之后的版本中,concurrent 包的加入,大大強(qiáng)化了 Java 語言的同步能力,concurrent 提供了很多不同類型的鎖機(jī)制可供擴(kuò)展。因此,要更好地觀測當(dāng)前的虛擬機(jī)狀況和不同線程的運行態(tài),去觀察虛擬機(jī)中的各種鎖,以及線程與鎖的關(guān)系是非常必要 的。很可惜的是,在過去的 JDK 中,我們并沒有非常方便的 API 以供使用。一個比較直接的檢測方式是查看線程的 stack trace,更為強(qiáng)大全面(但是也更復(fù)雜并且效率低下)的方案是得到一個 VM 所有對象的快照并查找之,這些策略的代價都比較大,而且往往需要編寫復(fù)雜的 Native 代碼。
JDK 6 里提供了一些相當(dāng)簡單的 API 來提供這個服務(wù)。首先了解兩個新類,LockInfo 和 MonitorInfo 這兩個類承載了鎖的信息。LockInfo 可以是任何的 Java 鎖,包括簡單 Java 鎖和 java.util.concurrent
包中所使用的鎖(包括 AbstractOwnableSynchronizer 和 Condition 的實現(xiàn)類/子類),而
MonitorInfo 是簡單的 Java 對象所代表的鎖。要檢測一個線程所擁有的鎖和等待的鎖,首先,要得到一個線程的
ThreadInfo,然后可以簡單地調(diào)用:
-
getLockedMonitors()
返回一個所有當(dāng)前線程已經(jīng)掌握的鎖對象的列表。
-
getLockedSynchronizers()
對于使用 concurrent 包的線程,返回一個該線程所掌握的“ownable synchronizer”(即 AbstractOwnableSynchronizer 及其子類)所組成的列表。
-
getLockInfo()
當(dāng)前線程正在等待的那個鎖對象的信息就可以知道線程所有的鎖信息。通過這些鎖信息,我們很方便的可以知道當(dāng)前虛擬機(jī)的所有線程的鎖信息。由此,我們還可以推導(dǎo)出更多的信息。
死 鎖檢測一直以來是軟件工程師所重視的,顯然一個死鎖的系統(tǒng)永遠(yuǎn)是工程師最大的夢魘。Java 程序的死鎖檢測也一直以來是 Java 程序員所頭痛的。為了解決線程間死鎖問題,一般都有預(yù)防(代碼實現(xiàn)階段)和死鎖后恢復(fù)(運行時)兩種方式。以前 Java 程序員都重視前者,因為在運行態(tài)再來檢測和恢復(fù)系統(tǒng)是相當(dāng)麻煩的,缺少許多必要的信息;但是,對于一些比較復(fù)雜的系統(tǒng),采取后者或者運行時調(diào)試死鎖信息也 是非常重要的。由上面所說,現(xiàn)在我們已經(jīng)可以知道每一個線程所擁有和等待的鎖,因此要計算出當(dāng)前系統(tǒng)中是否有死鎖的線程也是可行的了。當(dāng)然,Java 6 里面也提供了一個 API 來完成這個功能,即:
-
ThreadMXBean.findDeadlockedThreads()
這個函數(shù)的功能就是檢測出當(dāng)前系統(tǒng)中已經(jīng)死鎖的線程。當(dāng)然,這個功能復(fù)雜,因此比較費時。基本上僅僅將之用于調(diào)試,以便對復(fù)雜系統(tǒng)線程調(diào)用的改進(jìn)。
![]() ![]() |
![]()
|
JMX 在 Java SE 5/6 中的功能已經(jīng)相當(dāng)強(qiáng)大,但是距離 Java 程序開發(fā)人員的要求還是有一段距離,因此 Sun 公司已經(jīng)向 JCP 提出了 JSR 255 (JMX API 2.0 版本)來擴(kuò)充和進(jìn)一步發(fā)展 JMX,并希望這個 JSR 將在 Java SE 7 中實現(xiàn)。在這個文檔中,新的 JMX 2.0 將著重于:
- 對 management 模型的優(yōu)化,并提供更好的支持;加入了比如 annotation 等等的新特性;
- 對 JMX 定義的優(yōu)化,在進(jìn)一步強(qiáng)化 MBean 擴(kuò)充性好的優(yōu)點的同時,盡量改變(用戶普遍認(rèn)為的)MBean 很難實現(xiàn)的缺點;
- 對非 Java 平臺客戶端的支持。這將是一個令人振奮的新特性;
具體的擴(kuò)展可能包括:
- 層次性的命名域(Hierarchical namespace);
- 新的事件服務(wù)功能;
- 對 locales 的新支持;
- 為 MBean 啟用 annotation 服務(wù);
- 也可以使用用戶類型的 mapping 了;
可以看到,JMX 的進(jìn)一步發(fā)展主要關(guān)注的是可擴(kuò)展性、動態(tài)性和易用性等 Java 用戶非常關(guān)注的方面。
![]() ![]() |
![]()
|
在 Java SE 5 出現(xiàn)的 JMX 在 Java SE 6 中有了更多的功能和擴(kuò)展能力,這很好地適應(yīng)了 Java 語言的發(fā)展和用戶的要求,讓 Java 的監(jiān)測、管理的的功能更加強(qiáng)大。
盡管千里冰封
依然擁有晴空
你我共同品味JAVA的濃香.