利用JMX統計遠程JAVA進程的CPU和Memory---jVM managerment API


從JAVA 5開始,JDK提供了一些JVM檢測的API,這就是有名的java.lang.management 包,包里提供了許多MXBean的接口類,可以很方便的獲取到JVM的內存、GC、線程、鎖、class、甚至操作系統層面的各種信息,本文就簡單的介紹 一種利用JMX對JAVA進程進行CPU、堆內存使用的監控。可能有人會覺得沒必要這樣做,因為用jconsole之類的工具都能做到,而且會比本文的例 子更詳細。但是有些時候將console不一定能監控到作為系統服務的java進程,我最近就不得不自己編碼去獲取遠程java進程的監控數據。希望能起 到拋磚引玉的作用吧。 

      首先,簡要介紹下JMX(Java Management Extensions),即JAVA管理擴展,用來監視和管理JVM以及其運行的操作系統。目前java平台主要提供了下圖所示的9個MXBean, 各個MXBean的作用根據類名大概能猜出幾分,具體可查API。


        java.lang.management包中的mxbean提供了基本的功能,在sum.com.management中對某些功能有所增強,當然我們也可以根據JMX規范提供自己的MXBean。

 

       下面我主要使用java.lang.management.MemoryMXBean和 sun.com.management.OperatingSystemMXBean分別對遠程JAVA進行內存和cpu的監控。根據需求5秒鍾讀取一次 數據,內存主要是已使用的Heap Memory,CPU主要就是使用率了。

 

       在使用OperatingSystemMXBean以及MemoryMXBean之前,首先必須得到JMXConnector並創建 MBeanServerConnnection,有了這個connection我們就可以利用ManagementFactory創建需要的MXBean 了,類依賴圖如下:



        示例代碼:

Java代碼   收藏代碼
  1. /* 
  2. * host: 遠程機器的ip地址 
  3. * port: 遠程java進程運行的jmxremote端口 
  4. */  
  5. JMXServiceURL serviceURL = new JMXServiceURL( host,port );  
  6. JMXConnector conn = JMXConnectorFactory.connect(serviceURL);  
  7. MBeanServerConnection mbs=conn.getMBeanServerConnection();  
  8.   
  9. //獲取遠程memorymxbean  
  10. MemoryMXBean memBean=ManagementFactory.newPlatformMXBeanProxy  
  11. (mbs,ManagementFactory.MEMORY_MXBEAN_NAME, MemoryMXBean.class);  
  12. //獲取遠程opretingsystemmxbean  
  13. com.sun.management.OperatingSystemMXBean opMXbean =   
  14. ManagementFactory.newPlatformMXBeanProxy(mbs,                 
  15. ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, OperatingSystemMXBean.class);  

 

       然后,采集memory的數據就比較簡單了,直接調用API獲取:

Java代碼   收藏代碼
  1. /**    Collect data every 5 seconds      */  
  2. try {  
  3.     TimeUnit.SECONDS.sleep(5);  
  4. catch (InterruptedException e) {  
  5.     logger.error("InterruptedException occurred while MemoryCollector sleeping...");  
  6. }  
  7.   
  8. MemoryUsage heap = memBean  
  9. etHeapMemoryUsage();  
  10. MemoryUsage nonHeap = memBean  
  11. etNonHeapMemoryUsage();  
  12. long heapSizeUsed = heap.getUsed();//堆使用的大小  
  13. long nonHeapSizeUsed = nonHeap.getUsed();  
  14. long heapCommitedSize = heap.getCommitted();  
  15. long nonHeapCommitedSize = nonHeap.getCommitted();  
  16.                   

      采集CPU利用率需要自己計算一下,因為API只提供了獲取cpu的使用時間,我得在兩次系統時間間隔內獲取兩次CPU的使用時間,得到在該時間間隔內cpu使用的時間,相除即得到CPU的使用率,當然誤差肯定存在。

計算cpu使用率代碼   收藏代碼
  1.         Long start = System.currentTimeMillis();  
  2.         long startT = opMXbean.getProcessCpuTime();  
  3.         /**    Collect data every 5 seconds      */  
  4.         try {  
  5.             TimeUnit.SECONDS.sleep(5);  
  6.         } catch (InterruptedException e) {  
  7.             logger.error("InterruptedException occurred while MemoryCollector sleeping...");  
  8.         }  
  9.         Long end = System.currentTimeMillis();  
  10.         long endT = opMXbean.getProcessCpuTime();  
  11. //end - start 即為當前采集的時間單元,單位ms  
  12. //endT - startT 為當前時間單元內cpu使用的時間,單位為ns  
  13. //所以:double ratio = (entT-startT)/1000000.0/(end-start)/opMXbean.getAvailableProcessors()  

 

      核心代碼就是這些了,當然,具體使用的話應該用單獨的線程分別取cpu、memory數據,讀取的數據需要寫文件或者畫圖,監控時間長的話還要定時的將這 些數據刷到磁盤文件或數據庫中,等,這些都是題外話了。這邊我寫到excel中,然后在excel中圖形展示,遠程監控的程序不方便展示,僅僅來監控一段模擬cpu正弦曲線的程序 ,來看看我監控到的數據(圖形)是否和預期一致,並與jconsole采到的有何差異:

被測程序,模擬cpu正弦曲線代碼   收藏代碼
  1. public class SinCpu {  
  2.   
  3.     public static final double TIME = 1000;  
  4.   
  5.     /**  
  6.      * @param args the command line arguments  
  7.      */  
  8.     public static void main(String[] args) throws InterruptedException {  
  9.             new Thread(new SinTask()).start();  
  10.     }  
  11.   
  12.       
  13.       
  14.    static class SinTask implements Runnable{  
  15.   
  16.         @Override  
  17.         public void run() {  
  18.             double x = 0;  
  19.             double y = 0;  
  20.   
  21.             while (true) {  
  22.                 y = (Math.sin(x) + 1) * TIME / 2;  
  23.                 doSomeSimpleWork(y);  
  24.                 x += 0.1;  
  25.                 try {  
  26.                     Thread.sleep((long) (TIME - y));  
  27.                 } catch (InterruptedException e) {  
  28.                     e.printStackTrace();  
  29.                 }  
  30.             }  
  31.         }  
  32.         private void doSomeSimpleWork(double y) {  
  33.             long startTime = System.currentTimeMillis();  
  34.             while ((System.currentTimeMillis() - startTime) < y) {  
  35.             }  
  36.         }  
  37.     }  
  38. }  

 

       監控結果基本和預期一樣,CPU數據圖呈現預期的正弦曲線:


        上圖中,第一個圖是從jconsole監控圖中截過來的,而下圖是我利用opMXBean計算獲得,兩個圖基本吻合,數據基本波動在0-25%是因為我測 試機器是四核的cpu,兩個圖像之間有位移是因為,我手動打開jconsole沒有我程序監控來的快,所以大概圖像平移下基本吻合。

        內存的測試應該比CPU還要准一些,這里就不貼了。Note: 如果監控的程序線程數量很大,cpu會有較明顯誤差,而且采的頻率不夠高的話可能有些點漏采。

 

最后:被測程序必須開放JMXREMOTE端口,具體使用參數:

-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=[開放的端口]
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

 

 

JVM本 身提供了一組管理的API,通過該API,我們可以獲取得到JVM內部主要運行信息,包括內存各代的數據、JVM當前所有線程及其棧相關信息等等。各種 JDK自帶的剖析工具,包括jps、jstack、jinfo、jstat、jmap、jconsole等,都是基於此API開發的。本篇對這部分內容進 行一個詳細的說明。

參考:http://java.sun.com/javase/6/docs/api/java/lang/management/package-summary.html
一、Management API
我們先看一下從Sun JVM我們可以獲取到哪些信息,如下圖(來自於JConsole的MBean部分的截圖):

JVM Management API

1.HotSpotDiagnostic:非標准的監控JMX,這塊是Sun JVM自帶的,主要提供了兩個功能

  • 修改JVM的啟動參數(譬如在不需要重啟的情況下設置-XX:+HeapDumpOnOutOfMemoryError參數使得JVM內存不足的時候自動dump出堆空間到文件提供后續分析)
  • Dump堆信息到文件,可以猜測jmap工具是基於此功能來完成的

我們通過com.sun.management.HotSpotDiagnosticMXBean定義了解其主要功能

public interface HotSpotDiagnosticMXBean { void dumpHeap(String s, boolean flag) throws IOException; List getDiagnosticOptions(); VMOption getVMOption(String s); void setVMOption(String s, String s1); } 

2.ClassLoading:加載的類的總體信息,我們可以通過此MBean獲取到JVM加載的類定義的總體信息,可以猜測JConsole的類 功能就 是通過此MBean來提供的。我們可以通過java.lang.management.ClassLoadingMXBean定義了解其提供的主要功能

public interface ClassLoadingMXBean { public long getTotalLoadedClassCount(); public int getLoadedClassCount(); public long getUnloadedClassCount(); public boolean isVerbose(); public void setVerbose(boolean value); } 

3.Compilation:提供JVM的JIT(Just In Time)編譯器(將bytecode編譯成native code)的信息,我們可以通過java.lang.management.CompilationMXBean定義了解其提供的主要功能

public interface CompilationMXBean { public java.lang.String getName(); public boolean isCompilationTimeMonitoringSupported(); public long getTotalCompilationTime(); } 

4.GarbageCollector:垃圾回收器信息,譬如在如上圖中,我們啟動的JVM會包含一個Copy垃圾回收器(用於Young Gen垃圾回收)和一個MarkAndSweep垃圾回收器(用於Tenured Gen垃圾回收)。我們可以通過java.lang.management.GarbageCollectorMXBean定義了解其提供的主要功能

public interface GarbageCollectorMXBean extends MemoryManagerMXBean { public long getCollectionCount(); public long getCollectionTime(); } 

java.lang.management.MemoryManagerMXBean定義是

public interface MemoryManagerMXBean { public String getName(); public boolean isValid(); public String[] getMemoryPoolNames(); } 

除了如上信息,Sun JVM在實現上還提供了一個額外的信息LastGCInfo,見com.sun.management.GarbageCollectorMXBean定義

public interface GarbageCollectorMXBean extends java.lang.management.GarbageCollectorMXBean { GcInfo getLastGcInfo(); } 

我們可以通過下面的截圖了解GcInfo包含的主要信息

JVM Management API

其中java.lang.management.MemoryUsage后續可以看說明
5.內存相關
可以猜測,JConsole的內存部分的功能都是通過此部分的相關Bean來完成的。
1)Memory/MemoryManager:內存塊相關信息,通過這MBean我們可以獲取到內存的總體信息,並可以通過提供的gc操作進行強制gc 的功能(System.gc())。我們可以通過java.lang.management.MemoryMXBean和 java.lang.management.MemoryManagerMXBean了解其主要提供的功能

public interface MemoryMXBean { public int getObjectPendingFinalizationCount(); public MemoryUsage getHeapMemoryUsage(); public MemoryUsage getNonHeapMemoryUsage(); public boolean isVerbose(); public void setVerbose(boolean value); public void gc(); } 

其中java.lang.management.MemoryUsage我們可以通過下圖來了解其提供的主要信息

JVM Management API

public interface MemoryManagerMXBean { public String getName(); public boolean isValid(); public String[] getMemoryPoolNames(); } 

2)MemoryPool:通過該MBean可以了解JVM各內存塊的信息,譬如對於Sun JVM,目前包括Eden Space、Suvivor Space、Tenured Gen、CodeCache、Perm Gen,可以猜測JConsole的內存監控功能就是通過此MBean來做到的。我們可以通過 java.lang.management.MemoryPoolMXBean了解其主要提供的功能

public interface MemoryPoolMXBean { public String getName(); public MemoryType getType(); public MemoryUsage getUsage(); public MemoryUsage getPeakUsage(); public void resetPeakUsage(); public boolean isValid(); public String[] getMemoryManagerNames(); public long getUsageThreshold(); public void setUsageThreshold(long threshold); public boolean isUsageThresholdExceeded(); public long getUsageThresholdCount(); public boolean isUsageThresholdSupported(); public long getCollectionUsageThreshold(); public void setCollectionUsageThreshold(long threhsold); public boolean isCollectionUsageThresholdExceeded(); public long getCollectionUsageThresholdCount(); public MemoryUsage getCollectionUsage(); public boolean isCollectionUsageThresholdSupported(); } 

6.系統運行信息
1)OperatingSystem:通過該MBean我們可以了解到JVM所運行在的操作系統上的一些相關信息,通過java.lang.management.OperatingSystemMXBean定義我們可以了解到其主要提供的功能

public interface OperatingSystemMXBean { public String getName(); public String getArch(); public String getVersion(); public int getAvailableProcessors(); public double getSystemLoadAverage(); } 

SunJVM在此基礎上提供更多的一些信息,可以通過com.sun.management.OperatingSystemMXBean了解一些額外可以獲取到的信息

public interface OperatingSystemMXBean extends java.lang.management.OperatingSystemMXBean { long getCommittedVirtualMemorySize(); long getTotalSwapSpaceSize(); long getFreeSwapSpaceSize(); long getProcessCpuTime(); long getFreePhysicalMemorySize(); long getTotalPhysicalMemorySize(); } 

2)Runtime:通過該MBean獲取獲取到JVM一些相關的信息,通過java.lang.management.RuntimeMXBean可以了解其主要提供的功能

public interface RuntimeMXBean { public String getName(); public String getVmName(); public String getVmVendor(); public String getVmVersion(); public String getSpecName(); public String getSpecVendor(); public String getSpecVersion(); public String getManagementSpecVersion(); public String getClassPath(); public String getLibraryPath(); public boolean isBootClassPathSupported(); public String getBootClassPath(); public java.util.List<String> getInputArguments(); public long getUptime(); public long getStartTime(); public java.util.Map<String, String> getSystemProperties(); } 

可以通過RuntimeMXBean.getUptime()和OperatingSystemMXBean. getProcessCpuTime()來計算JVM占用的系統CPU比例的情況,JConsole的CPU視圖就是通過這種方式計算的。
7.Threading:可以通過該MBean獲取線程信息,包括線程狀態、執行棧等。可以通過java.lang.management.ThreadMXBean了解其提供的主要功能

public interface ThreadMXBean { public int getThreadCount(); public int getPeakThreadCount(); public long getTotalStartedThreadCount(); public int getDaemonThreadCount(); public long[] getAllThreadIds(); public ThreadInfo getThreadInfo(long id); public ThreadInfo[] getThreadInfo(long[] ids); public ThreadInfo getThreadInfo(long id, int maxDepth); public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth); public boolean isThreadContentionMonitoringSupported(); public boolean isThreadContentionMonitoringEnabled(); public void setThreadContentionMonitoringEnabled(boolean enable); public long getCurrentThreadCpuTime(); public long getCurrentThreadUserTime(); public long getThreadCpuTime(long id); public long getThreadUserTime(long id); public boolean isThreadCpuTimeSupported(); public boolean isCurrentThreadCpuTimeSupported(); public boolean isThreadCpuTimeEnabled(); public void setThreadCpuTimeEnabled(boolean enable); public long[] findMonitorDeadlockedThreads(); public void resetPeakThreadCount(); public long[] findDeadlockedThreads(); public boolean isObjectMonitorUsageSupported(); public boolean isSynchronizerUsageSupported(); public ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, boolean lockedSynchronizers); public ThreadInfo[] dumpAllThreads(boolean lockedMonitors, boolean lockedSynchronizers); } 

二、編程獲取到JVM Manage信息
我們可以通過JMX的方式讀取到JVM Manage定義的MBean,如下是3種獲取方法
1.監控應用與被監控應用位於同一JVM

MBeanServer server = ManagementFactory.getPlatformMBeanServer();
RuntimeMXBean rmxb = ManagementFactory.newPlatformMXBeanProxy(server, "java.lang:type=Runtime", RuntimeMXBean.class); 

2.監控應用與被監控應用不位於同一JVM
1)首先在被監控的JVM的啟動參數中加入如下的啟動參數以啟JVM代理

-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=127.0.0.1:8000 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

2)連接到代理上

JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://127.0.0.1:8000/jmxrmi"); JMXConnector connector = JMXConnectorFactory.connect(url); RuntimeMXBean rmxb = ManagementFactory.newPlatformMXBeanProxy(connector .getMBeanServerConnection(),"java.lang:type=Runtime", RuntimeMXBean.class); 

3.監控應用與被監控應用不位於同一JVM但在同一物理主機上(2的特化情況,通過進程Attach)
我們使用JDK工具,如jmap、jstack等的時候,工具所在的JVM當然與被監控的JVM不是同一個,所以不能使用方式1,被監控的JVM一般也不 會在啟動參數中增加JMX的支持,所以方式2也沒有辦法。還好Sun JVM給我們提供了第3種非標准的方式,就是通過Attach到被監控的JVM進程,並在被監控的JVM中啟動一個JMX代理,然后使用該代理通過2的方 式連接到被監控的JVM的JMX上。下面是一個使用范例,由於里面使用到的知識涉及到Java Instrutment(JVMTI的一個技術的Java實現)和Attach API,因此此處不做詳細解析,在后續看完Java Instrutment和Attach API自然就會明白。(注意,僅在JDK6+中支持,另外,運行需要jdk的tools.jar包)

//Attach 到5656的JVM進程上,后續Attach API再講解
VirtualMachine virtualmachine = VirtualMachine.attach("5656"); //讓JVM加載jmx Agent,后續講到Java Instrutment再講解 String javaHome = virtualmachine.getSystemProperties().getProperty("java.home"); String jmxAgent = javaHome + File.separator + "lib" + File.separator + "management-agent.jar"; virtualmachine.loadAgent(jmxAgent, "com.sun.management.jmxremote"); //獲得連接地址 Properties properties = virtualmachine.getAgentProperties(); String address = (String)properties.get("com.sun.management.jmxremote.localConnectorAddress"); //Detach virtualmachine.detach(); JMXServiceURL url = new JMXServiceURL(address); JMXConnector connector = JMXConnectorFactory.connect(url); RuntimeMXBean rmxb = ManagementFactory.newPlatformMXBeanProxy(connector .getMBeanServerConnection(), "java.lang:type=Runtime",RuntimeMXBean.class); 

三、結束語
可以看到,通過標准的接口,我們已經可以獲得運行的JVM很詳細的信息,從運行JVM、操作系統,到內存、GC和線程,通過這些標准的接口我們已經可以對 JVM進行功能完善的監控。但是僅此是不夠的,這部分接口描述的主要是JVM的總體性的信息,而無法提供更多的細節。在下一部分,我們將使用JPDA來更 深入地了解JVM內部信息更細節的信息,並了解我們如何通過JVM TI實現自動的性能監控


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM