千里冰封
          JAVA 濃香四溢
          posts - 151,comments - 2801,trackbacks - 0

          前言

          在 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 和系統(tǒng)管理

          管理系統(tǒng)(Management System)

          要 了解 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 資源配個翻譯就可以了。

          管理系統(tǒng)的構(gòu)架


          圖 1. 管理系統(tǒng)構(gòu)架
          圖 1. 管理系統(tǒng)構(gòu)架

          上圖分析了管理系統(tǒng)的基本構(gòu)架模式。其中 Agent / SubAgent 起到的就是翻譯的作用:把 IT 資源報告的消息以管理系統(tǒng)能理解的方式傳送出去。

          也許讀者有會問,為什么需要 Agent 和 SubAgent 兩層體系呢?這里有兩個現(xiàn)實的原因:

          1. 管理系統(tǒng)一般是一個中央控制的控制軟件,而 SubAgent 直接監(jiān)控一些資源,往往和這些資源分布在同一物理位置。當(dāng)這些 SubAgent 把狀態(tài)信息傳輸?shù)焦芾硐到y(tǒng)或者傳達(dá)管理系統(tǒng)的控制指令的時候,需要提供一些網(wǎng)絡(luò)傳輸?shù)墓δ堋?/li>
          2. 管理系統(tǒng)的消息是有一定規(guī)范的,消息的翻譯本身是件復(fù)雜而枯燥的事情。

          一般來說,管理系統(tǒng)會將同一物理分布或者功能類似的 SubAgent 分組成一組,由一個共用的 Agent 加以管理。在這個 Agent 里封裝了 1 和 2 的功能。

          JMX 和管理系統(tǒng)

          JMX 既是 Java 管理系統(tǒng)的一個標(biāo)準(zhǔn),一個規(guī)范,也是一個接口,一個框架。圖 2 展示了 JMX 的基本架構(gòu)。


          圖 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.managementjavax.management 包。java.lang.management 包提供了基本的 VM 監(jiān)控功能,而 javax.management 包則向用戶提供了擴(kuò)展功能。





          回頁首


          JMX 的基本框架

          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

          標(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):


          package standardbeans;
          public class ServerImpl {
              
          public final long startTime;
              
          public ServerImpl() {
                  startTime 
          = System.currentTimeMillis();
              }
          }
          然后,我們打算使用一個標(biāo)準(zhǔn) MBean,ServerMonitor 來監(jiān)控 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 規(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)的例子:

          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 已經(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 的屬性的。

          動態(tài) 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 的對外接口。

          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;
              }
              
              
          //實現(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 },
                                  
          nullnull);                
                      } 
          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;
              }   
          }

          其它動態(tài) MBean

          另外還有兩類 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 接口中特殊的類。

          Model Bean

          然 而,普通的動態(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 端:

          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;
              }
          }

          然后我們對它的監(jiān)測如下:

          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));        
                  Server server 
          = new Server();
                  serverMBean.setManagedResource(server, 
          "ObjectReference");
                  
                  ObjectInstance registeredServerMBean 
          =
                      mBeanServer.registerMBean((Object) serverMBean, serverMBeanName);
                  
                  serverMBean.invoke(
          "start",nullnull);
                  
                  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 的過程也是下面幾步:

          1. 創(chuàng)建一個 MBServer:mBeanServe
          2. 獲得管理資源用的 MBean:serverBean
          3. 給這個 MBean 一個 ObjectName:serverMBeanName
          4. 將 serverBean 以 serverMBeanName 注冊到 mBeanServer 上去

          唯一不同的是,ModelMBean 需要額外兩步:

          1.serverMBean.setModelMBeanInfo(getModelMBeanInfoForServer(serverMBeanName));
          2.serverMBean.setManagedResource(server, "ObjectReference");

          第一步用于提供 serverMBean 的元數(shù)據(jù),主要包括以下兩類

          1. 類似于普通的動態(tài) MBean,需要 MBean 的 Attribute、Invocation、Notification 的類型/反射信息,諸如返回類型、參數(shù)類型和相關(guān)的 get/set 方法等。這里將不再贅述。
          2. 關(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 重寫了這個策略特性。

          在上面的例子里,這一個語句:

          serverMMBeanInfo.setMBeanDescriptor(serverDescription);

          給整個 serverMBeanInfo 設(shè)了一個策略描述 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 數(shù)據(jù)了。





          回頁首


          虛擬機(jī)檢測

          JMX 與虛擬機(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ì)介紹。

          JMX 提供的虛擬機(jī)檢測 API

          檢測虛擬機(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)存值等等。

          • 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)系非常緊密。





          回頁首


          Java 6 中的 API 改進(jì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)。





          回頁首


          未來的發(fā)展

          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)注的方面。





          回頁首


          總結(jié)

          在 Java SE 5 出現(xiàn)的 JMX 在 Java SE 6 中有了更多的功能和擴(kuò)展能力,這很好地適應(yīng)了 Java 語言的發(fā)展和用戶的要求,讓 Java 的監(jiān)測、管理的的功能更加強(qiáng)大。











          盡管千里冰封
          依然擁有晴空

          你我共同品味JAVA的濃香.
          posted on 2007-11-15 08:52 千里冰封 閱讀(1562) 評論(2)  編輯  收藏 所屬分類: 轉(zhuǎn)載文章

          FeedBack:
          # re: Java SE 6 新特性: JMX 與系統(tǒng)管理(轉(zhuǎn))
          2007-11-15 15:12 | 憑欄觀海
          不錯,好文,尤其在介紹IT管理原理的時候  回復(fù)  更多評論
            
          # re: Java SE 6 新特性: JMX 與系統(tǒng)管理(轉(zhuǎn))
          2007-11-15 17:48 | 潮水的諾言
          不錯,記號,有空詳細(xì)看  回復(fù)  更多評論
            
          主站蜘蛛池模板: 石台县| 同德县| 陇川县| 潞城市| 根河市| 四会市| 贵德县| 浦江县| 苍南县| 曲阜市| 马关县| 泸西县| 习水县| 南丰县| 防城港市| 永顺县| 平南县| 渑池县| 新沂市| 天祝| 沾化县| 突泉县| 班戈县| 赤壁市| 专栏| 钟山县| 临泽县| 邛崃市| 房山区| 金华市| 封丘县| 蓝田县| 阿拉善盟| 汝州市| 哈密市| 石景山区| 洪洞县| 铁岭市| 沽源县| 栾城县| 津南区|