在 Java 程序的運行過程中,對 JVM 和系統的監測一直是 Java 開發人員在開發過程所需要的。一直以來,Java 開發人員必須通過一些底層的 JVM API,比如 JVMPI 和 JVMTI 等,才能監測 Java 程序運行過程中的 JVM 和系統的一系列情況,這種方式一直以來被人所詬病,因爲這需要大量的 C 程序和 JNI 調用,開發效率十分低下。於是出現了各種不同的專門做資源管理的程序包。爲了解決這個問題,Sun 公司也在其 Java SE 5 版本中,正式提出了 Java 管理擴展(Java Management Extensions,JMX)用來管理檢測 Java 程序(同時 JMX 也在 J2EE 1.4 中被髮布)。
JMX 的提出,讓 JDK 中開發自檢測程序成爲可能,也提供了大量輕量級的檢測 JVM 和運行中對象/線程的方式,從而提高了 Java 語言自己的管理監測能力。
|
要了解 JMX,我們就必須對當前的 IT 管理系統有一個初步的瞭解。隨着企業 IT 規模的不斷增長,IT 資源(IT resource)數量不斷增加,IT 資源的分佈也越來越分散。可以想象,甚至對於一家只有幾百臺 PC 公司的 IT 管理人員來說,分發一個安全補丁並且保證其在每臺 PC 上的安裝,如果只依賴人工來完成那簡直就是一場噩夢。這樣,IT 管理系統就應運而生。
然而,CPU、網卡、存儲陣列是 IT 資源;OS、MS Office、Oracle database、IBM Websphere 也是 IT 資源。IT 管理系統若要對這些 IT 資源進行管理,就必須對這些管理對象有所瞭解:形形色色的 IT 資源就像是說着不同語言的人:Oralce 數據庫表達內存緊張的方式和 Window XP 是絕然不同的, 而 IT 管理系統就像建造通天塔的經理,必須精通所有的語言, 這幾乎是一個不可能完成的任務。難道 IT 管理系統是另外一個通天塔嗎?當然不是!其實我們只要給每個 IT 資源配個翻譯就可以了。
圖 1. 管理系統構架
上圖分析了管理系統的基本構架模式。其中 Agent / SubAgent 起到的就是翻譯的作用:把 IT 資源報告的消息以管理系統能理解的方式傳送出去。
也許讀者有會問,爲什麼需要 Agent 和 SubAgent 兩層體系呢?這裏有兩個現實的原因:
- 管理系統一般是一箇中央控制的控制軟件,而 SubAgent 直接監控一些資源,往往和這些資源分佈在同一物理位置。當這些 SubAgent 把狀態信息傳輸到管理系統或者傳達管理系統的控制指令的時候,需要提供一些網絡傳輸的功能。
- 管理系統的消息是有一定規範的,消息的翻譯本身是件複雜而枯燥的事情。
一般來說,管理系統會將同一物理分佈或者功能類似的 SubAgent 分組成一組,由一個共用的 Agent 加以管理。在這個 Agent 裏封裝了 1 和 2 的功能。
JMX 既是 Java 管理系統的一個標準,一個規範,也是一個接口,一個框架。圖 2 展示了 JMX 的基本架構。
圖 2. JMX 構架
和其它的資源系統一樣,JMX 是管理系統和資源之間的一個接口,它定義了管理系統和資源之間交互的標準。javax.management.MBeanServer
實現了 Agent 的功能,以標準的方式給出了管理系統訪問 JMX 框架的接口。而 javax.management.MBeans
實現了 SubAgent 的功能,以標準的方式給出了 JMX 框架訪問資源的接口。而從類庫的層次上看,JMX 包括了核心類庫 java.lang.management
和 javax.management
包。java.lang.management
包提供了基本的 VM 監控功能,而 javax.management
包則向用戶提供了擴展功能。
|
JMX 使用了 Java Bean 模式來傳遞信息。一般說來,JMX 使用有名的 MBean,其內部包含了數據信息,這些信息可能是:應用程序配置信息、模塊信息、系統信息、統計信息等。另外,MBean 也可以設立可讀寫的屬性、直接操作某些函數甚至啓動 MBean 可發送的 notification 等。MBean 包括 Standard,MXBean,Dynamic,Model,Open 等幾種分類,其中最簡單是標準 MBean 和 MXBean,而我們使用得最多的也是這兩種。MXBean 主要是 java.lang.management
使用較多,將在下一節中介紹。我們先了解其他一些重要的 MBean 的種類。
標準 MBean 是最簡單的一類 MBean,與動態 Bean 不同,它並不實現 javax.management
包中的特殊的接口。說它是標準 MBean, 是因爲其向外部公開其接口的方法和普通的 Java Bean 相同,是通過 lexical,或者說 coding convention 進行的。下面我們就用一個例子來展現,如何實現一個標準 MBean 來監控某個服務器 ServerImpl 狀態的。ServerImpl 代表了用來演示的某個 Server 的實現:
package standardbeans; public class ServerImpl { public final long startTime; public ServerImpl() { startTime = System.currentTimeMillis(); } } |
然後,我們打算使用一個標準 MBean,ServerMonitor 來監控 ServerImpl:
package standardbeans; 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 規定了標準 MBean 也要實現一個接口,所有向外界公開的方法都要在這個接口中聲明。否則,管理系統就不能從中獲得相應的信息。此外,該接口的名字也有一定的規範:即在標準 MBean 類名之後加上“MBean”後綴。若 MBean 的類名叫做 MBeansName 的話,對應的接口就要叫做 MBeansNameMBean。
對於管理系統來說,這些在 MBean 中公開的方法,最終會被 JMX 轉化成屬性(Attribute)、監聽(Listener)和調用(Invoke)的概念。如果讀者對 Java Bean 有一些瞭解的話,不難看出,public long getUpTime()
對應了 Bean 中的一個稱爲“upTime”的只讀屬性。
下面我們就看一個模擬管理系統的例子:
package standardbeans; 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 已經提供了一個默認實現,可以通過 MBServerFactory.createMBeanServer()
獲得。每個資源監控者(MBean)一般都會有名稱(ObjectName), 登記在 MBServer 內部的一個 Repository 中。注意,這個 ObjectName 對於每一個 MBServer 必須是唯一的,只能對應於一個 MBean。(讀者有興趣的話,可以試着再給 mBeanServer 註冊一個同名的 objectName,看看會怎麼樣。) 上述例子是在 init()
方法中完成向 MBeanServer 註冊工作的。
在管理過程中,管理系統並不與資源或者 SubAgent 直接打交道,也就是說,這裏不會直接引用到 MBean。而是通過 MBeanServer 的 getAttribute
方法取得對應 MBean 的屬性的。
但是對於很多已有的 SubAgent 實現,其 Coding Convention 並不符合標準 MBean 的要求。重構所有這些 SubAgent 以符合標準 MBean 標準既費力也不實際。JMX 中給出了動態(Dynamic) MBean 的概念,MBServer 不再依據 Coding Convention 而是直接查詢動態 MBean 給出的元數據(meta data)以獲得 MBean 的對外接口。
package dynamicbeans; 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; } //實現獲取被管理的 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]); //用反射獲得構造方法 Constructor constructor = cls.getConstructor(new Class[] {ServerImpl.class}); //關於 "upTime" 屬性的元信息:名稱爲 UpTime,只讀屬性(沒有寫方法)。 MBeanAttributeInfo upTimeMBeanAttributeInfo = new MBeanAttributeInfo( "UpTime", "The time span since server start", readMethod, null); //關於構造函數的元信息 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。實際上它們也都是動態 MBean。
Open MBean 與其它動態 MBean 的唯一區別在於,前者對其公開接口的參數和返回值有所限制 —— 只能是基本類型或者 javax.management.openmbean
包內的 ArrayType、CompositeType、TarbularType 等類型。這主要是考慮到管理系統的分佈,很可能遠端管理系統甚至 MBServer 層都不具有 MBean 接口中特殊的類。
然而,普通的動態 Bean 通常缺乏一些管理系統所需要的支持:比如持久化 MBean 的狀態、日誌記錄、緩存等等。如果讓用戶去一一實現這些功能確實是件枯燥無聊的工作。爲了減輕用戶的負擔,JMX 提供商都會提供不同的 ModelBean 實現。其中有一個接口是 Java 規範中規定所有廠商必須實現的:javax.management.modelmbean.RequiredModelBean
。通過配置 Descriptor 信息,我們可以定製這個 Model Bean, 指定哪些 MBean 狀態需要記入日誌、如何記錄以及是否緩存某些屬性、緩存多久等等。這裏,我們以 RequiredModelBean 爲例討論 ModelBean。比如,我們先來看一個例子,首先是 server 端:
package modelmbean; public class Server { private long startTime; public Server() { } public int start(){ startTime = System.currentTimeMillis(); return 0; } public long getUpTime(){ return System.currentTimeMillis() - startTime; } } |
然後我們對它的監測如下:
package modelmbean; 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)); |-------10--------20--------30--------40--------50--------60--------70--------80--------9| |-------- XML error: The previous line is longer than the max of 90 characters ---------| 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 的過程也是下面幾步:
- 創建一個 MBServer:mBeanServe
- 獲得管理資源用的 MBean:serverBean
- 給這個 MBean 一個 ObjectName:serverMBeanName
- 將 serverBean 以 serverMBeanName 註冊到 mBeanServer 上去
唯一不同的是,ModelMBean 需要額外兩步:
1.serverMBean.setModelMBeanInfo(getModelMBeanInfoForServer(serverMBeanName)); 2.serverMBean.setManagedResource(server, "ObjectReference"); |
第一步用於提供 serverMBean 的元數據,主要包括以下兩類
- 類似於普通的動態 MBean,需要 MBean 的 Attribute、Invocation、Notification 的類型/反射信息,諸如返回類型、參數類型和相關的 get/set 方法等。這裏將不再贅述。
- 關於緩存、持久化以及日誌等的策略。後面我們將介紹一些這方面的信息。
第二步指出了 ServerMBean 管理的對象,也就是說,從元數據中得到的 Method 將施加在哪個 Object 上。需要指出的是 setManagedResource(Object o, String type);
中第二個參數是 Object 類型,可以是 "ObjectReference"、"Handle"、"IOR"、"EJBHandle" 或 "RMIReference"。目前 SE 中的實現只支持 "ObjectReference"。筆者認爲後面幾種類型是爲了將來 JMX 管理對象擴展而設定的,可能將來 Model Bean 不僅可以管理 Plain Java Object(POJO),還可能管理 Native Resource, 並給諸如 EJB 和 RMI 對象的管理提供更多的特性。
Model Bean 與普通動態 Bean 區別在於它的元數據類型 ModelMBeanInfo 擴展了前者的 MBeanInfo,使得 ModelMBeanOperationInfo、ModelMBeanConstructor_Info、ModelMBeanAttributeInfo 和 ModelMBeanNotificationInfo 都有一個額外的元數據:javax.management.Descriptor
,它是用來設定 Model Bean 策略的。數據的存儲是典型的 "key-value" 鍵值對。不同的 Model Bean 實現,以及不同的 MBeanFeatureInfo 支持不同的策略特性。下面我們就以 Attribute 爲例,看一下 RequiredModelBean 支持的策略。
首先,它最重要的 Descriptor 主要是 name、displayName 和 descriptorType,其中 name 是屬性名稱。"name" 要與對應 ModelMBeanAttributeInfo 的 name 相同。descriptorType 必須是 "attribute"。
另外,value、default、legalValues "value" 是用來設定初始值的,"default" 指當不能從 resource 中獲得該屬性時的默認返回值,"legalValues" 是一組合法的屬性數據。它並不用來保證 setAttribute 的數據一致性,而是在 UI 系統,如 JConsole 中提示用戶可能的數據輸入。
在屬性訪問的 getMethod, setMethod 方法上,事實上所有對屬性的訪問都會被 delegate 給同一 MBeanInfo 中特定的 Operation。 getMethod/setMethod 給出了對應的 ModelMBeanOperationInfo 名稱。
還有一些額外的屬性,比如:persistPolicy, persistPeriod 是代表了持久化策略;currencyTimeLimit, lastUpdatedTimeStamp 緩存策略;iterable 屬性是否必須使用 iterate 來訪問。默認爲否;protocolMap 定義了與第三方系統有關的數據轉換的 data model;visibility 定義了與第三方 UI 系統有關的 MBean 如何顯示的策略;presentationString 也是定義了與第三方 UI 系統有關的 MBean 如何顯示策略,比如 "presentation=server.gif"。
事實上,策略特性有兩個層次的作用域:整個 Model Bean 和特定的 MBeanFeature。
Model Bean 的策略描述會被施加到該 Model Bean 的所有 MBeanFeature 上去,除非該 MBeanFeature 重寫了這個策略特性。
在上面的例子裏,這一個語句:
serverMMBeanInfo.setMBeanDescriptor(serverDescription); |
給整個 serverMBeanInfo 設了一個策略描述 serverDescription,其中用 "currencyTimeLimit=10" 指出屬性的緩存時間是 10 秒。所以,在 Main 方法中,兩次 serverMBean.getAttribute("upTime");之間的間隔小於 10 秒就會得到同樣的緩存值。
如果我們不想讓 "upTime" 這個屬性被緩存,我們可以在它的策略描述中加入 "currencyTimeLimit=-1":
Descriptor upTime = new DescriptorSupport( 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 數據了。
|
JMX 的提出,爲 Java 虛擬機提供了 Java 層上的檢測機制。J2SE 中,新提出的 java.lang.management
包即是 JMX 在 JDK 的一個應用,它提供了大量的有用的接口,通過 MBean 方式,提供了對 Java 虛擬機和運行時遠端的監控和檢測方式,來幫助用戶來檢測本地或者遠端的虛擬機的運行情況。有了 JMX 之後,我們可以設計一個客戶端,來檢測遠端一個正在運行的虛擬機中的線程數、線程當前的 Stack、內存管理、GC 所佔用的時間、虛擬機中的對象和當前虛擬機參數等重要的參數和運行時信息。JMX 另外的一個重要功能是對配置信息的檢測和再配置。比如,我們可以在遠端查看和修改當前 JVM 的 verbose 參數,以達到動態管理的目的。甚至,我們可以在遠端指揮 JVM 做一次 GC,這在下文中有詳細介紹。
檢測虛擬機當前的狀態總是 Java 開放人員所關心的,也正是因爲如此,出現了大量的 profiler 工具來檢測當前的虛擬機狀態。從 Java SE 5 之後,在 JDK 中,我們有了一些 Java 的虛擬機檢測 API,即 java.lang.management
包。Management 包裏面包括了許多 MXBean 的接口類和 LockInfo、MemoryUsage、MonitorInfo 和 ThreadInfo 等類。從名字可以看出,該包提供了虛擬機內存分配、垃圾收集(GC)情況、操作系統層、線程調度和共享鎖,甚至編譯情況的檢測機制。這樣一來,Java 的開發人員就可以很簡單地爲自己做一些輕量級的系統檢測,來確定當前程序的各種狀態,以便隨時調整。
要獲得這些信息,我們首先通過 java.lang.management.ManagementFactory
這個工廠類來獲得一系列的 MXBean。包括:
- ClassLoadingMXBean
ClassLoadMXBean 包括一些類的裝載信息,比如有多少類已經裝載/卸載(unloaded),虛擬機類裝載的 verbose 選項(即命令行中的 Java –verbose:class 選項)是否打開,還可以幫助用戶打開/關閉該選項。
- CompilationMXBean
CompilationMXBean 幫助用戶瞭解當前的編譯器和編譯情況,該 mxbean 提供的信息不多。
- GarbageCollectorMXBean
相對於開放人員對 GC 的關注程度來說,該 mxbean 提供的信息十分有限,僅僅提供了 GC 的次數和 GC 花費總時間的近似值。但是這個包中還提供了三個的內存管理檢測類:MemoryManagerMXBean,MemoryMXBean 和 MemoryPoolMXBean。
- MemoryManagerMXBean
這個類相對簡單,提供了內存管理類和內存池(memory pool)的名字信息。
- MemoryMXBean
這個類提供了整個虛擬機中內存的使用情況,包括 Java 堆(heap)和非 Java 堆所佔用的內存,提供當前等待 finalize 的對象數量,它甚至可以做 gc(實際上是調用 System.gc)。
- MemoryPoolMXBean
該信息提供了大量的信息。在 JVM 中,可能有幾個內存池,因此有對應的內存池信息,因此,在工廠類中,getMemoryPoolMXBean() 得到是一個 MemoryPoolMXBean 的 list。每一個 MemoryPoolMXBean 都包含了該內存池的詳細信息,如是否可用、當前已使用內存/最大使用內存值、以及設置最大內存值等等。
- MemoryManagerMXBean
- OperatingSystemMXBean
該類提供的是操作系統的簡單信息,如構架名稱、當前 CPU 數、最近系統負載等。
- RuntimeMXBean
運行時信息包括當前虛擬機的名稱、提供商、版本號,以及 classpath、bootclasspath 和系統參數等等。
- ThreadMXBean
在 Java 這個多線程的系統中,對線程的監控是相當重要的。ThreadMXBean 就是起到這個作用。ThreadMXBean 可以提供的信息包括各個線程的各種狀態,CPU 佔用情況,以及整個系統中的線程狀況。從 ThreadMXBean 可以得到某一個線程的 ThreadInfo 對象。這個對象中則包含了這個線程的所有信息。
我們知道,management 和底層虛擬機的關係是非常緊密的。其實,有一些的是直接依靠虛擬機提供的公開 API 實現的,比如 JVMTI;而另外一些則不然,很大一塊都是由虛擬機底層提供某些不公開的 API / Native Code 提供的。這樣的設計方式,保證了 management 包可以提供足夠的信息,並且使這些信息的提供又有足夠的效率;也使 management 包和底層的聯繫非常緊密。
|
Management 在 Java SE 5 被提出之後,受到了歡迎。在 Java 6 當中,這個包提供更多的 API 來更好地提供信息。
OperatingSystemMXBean. getSystemLoadAverage()
Java 程序通常關注是虛擬機內部的負載、內存等狀況,而不考慮整個系統的狀況。但是很多情況下,Java 程序在運行過程中,整個計算機系統的系統負荷情況也會對虛擬機造成一定的影響。隨着 Java 的發展,Java 程序已經覆蓋了各個行業,這一點也必須得到關注。在以前,利用 Native 代碼來檢測系統負載往往是唯一的選擇,但是在 Java 6 當中,JDK 自己提供了一個輕量級的系統負載檢測 API,即 OperatingSystemMXBean.getSystemLoadAverage()
。
當然這個 API 事實上僅僅返回一個對前一分鐘系統負載的簡單的估測。它設計的主要目標是簡單快速地估測當前系統負荷,因此它首先保證了這個 API 的效率是非常高的;也因爲如此,這個 API 事實上並不適用於所有的系統。
我們知道,同步是 Java 語言很重要的一個特性。在 Java SE 中,最主要的同步機制是依靠 synchronize 關鍵字對某一個對象加鎖實現的;在 Java SE 5 之後的版本中,concurrent 包的加入,大大強化了 Java 語言的同步能力,concurrent 提供了很多不同類型的鎖機制可供擴展。因此,要更好地觀測當前的虛擬機狀況和不同線程的運行態,去觀察虛擬機中的各種鎖,以及線程與鎖的關係是非常必要的。很可惜的是,在過去的 JDK 中,我們並沒有非常方便的 API 以供使用。一個比較直接的檢測方式是查看線程的 stack trace,更爲強大全面(但是也更復雜並且效率低下)的方案是得到一個 VM 所有對象的快照並查找之,這些策略的代價都比較大,而且往往需要編寫複雜的 Native 代碼。
JDK 6 裏提供了一些相當簡單的 API 來提供這個服務。首先了解兩個新類,LockInfo 和 MonitorInfo 這兩個類承載了鎖的信息。LockInfo 可以是任何的 Java 鎖,包括簡單 Java 鎖和 java.util.concurrent
包中所使用的鎖(包括 AbstractOwnableSynchronizer 和 Condition 的實現類/子類),而 MonitorInfo 是簡單的 Java 對象所代表的鎖。要檢測一個線程所擁有的鎖和等待的鎖,首先,要得到一個線程的 ThreadInfo,然後可以簡單地調用:
getLockedMonitors()
返回一個所有當前線程已經掌握的鎖對象的列表。
getLockedSynchronizers()
對於使用 concurrent 包的線程,返回一個該線程所掌握的“ownable synchronizer”(即 AbstractOwnableSynchronizer 及其子類)所組成的列表。
getLockInfo()
當前線程正在等待的那個鎖對象的信息就可以知道線程所有的鎖信息。通過這些鎖信息,我們很方便的可以知道當前虛擬機的所有線程的鎖信息。由此,我們還可以推導出更多的信息。
死鎖檢測一直以來是軟件工程師所重視的,顯然一個死鎖的系統永遠是工程師最大的夢魘。Java 程序的死鎖檢測也一直以來是 Java 程序員所頭痛的。爲了解決線程間死鎖問題,一般都有預防(代碼實現階段)和死鎖後恢復(運行時)兩種方式。以前 Java 程序員都重視前者,因爲在運行態再來檢測和恢復系統是相當麻煩的,缺少許多必要的信息;但是,對於一些比較複雜的系統,採取後者或者運行時調試死鎖信息也是非常重要的。由上面所說,現在我們已經可以知道每一個線程所擁有和等待的鎖,因此要計算出當前系統中是否有死鎖的線程也是可行的了。當然,Java 6 裏面也提供了一個 API 來完成這個功能,即:
ThreadMXBean.findDeadlockedThreads()
這個函數的功能就是檢測出當前系統中已經死鎖的線程。當然,這個功能複雜,因此比較費時。基本上僅僅將之用於調試,以便對複雜系統線程調用的改進。
|
JMX 在 Java SE 5/6 中的功能已經相當強大,但是距離 Java 程序開發人員的要求還是有一段距離,因此 Sun 公司已經向 JCP 提出了 JSR 255 (JMX API 2.0 版本)來擴充和進一步發展 JMX,並希望這個 JSR 將在 Java SE 7 中實現。在這個文檔中,新的 JMX 2.0 將着重於:
- 對 management 模型的優化,並提供更好的支持;加入了比如 annotation 等等的新特性;
- 對 JMX 定義的優化,在進一步強化 MBean 擴充性好的優點的同時,儘量改變(用戶普遍認爲的)MBean 很難實現的缺點;
- 對非 Java 平臺客戶端的支持。這將是一個令人振奮的新特性;
具體的擴展可能包括:
- 層次性的命名域(Hierarchical namespace);
- 新的事件服務功能;
- 對 locales 的新支持;
- 爲 MBean 啓用 annotation 服務;
- 也可以使用用戶類型的 mapping 了;
可以看到,JMX 的進一步發展主要關注的是可擴展性、動態性和易用性等 Java 用戶非常關注的方面。
|
在 Java SE 5 出現的 JMX 在 Java SE 6 中有了更多的功能和擴展能力,這很好地適應了 Java 語言的發展和用戶的要求,讓 Java 的監測、管理的的功能更加強大。
摘自:http://www.ibm.com/developerworks/cn/java/j-lo-jse63/index.html