zoukankan      html  css  js  c++  java
  • 利用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实现自动的性能监控

  • 相关阅读:
    HTML的初体验
    HTML兼容问题——HACK技术
    JavaScript 小试牛刀
    JavaScript 初识
    HTML5+CSS3 代码简写篇
    原生JavaScript技巧
    Java1变量数据类型和运算符
    java6循环结构二
    java3选择结构一
    java2变量数据类型和运算符
  • 原文地址:https://www.cnblogs.com/zhengah/p/4962352.html
Copyright © 2011-2022 走看看