1. 程式人生 > >使用JMX來獲取CPU利用率和GC 所佔用的CPU利用率

使用JMX來獲取CPU利用率和GC 所佔用的CPU利用率

這篇部落格會介紹如何通過JMX 來獲取JAVA 程序佔用的CPU利用率和GC所佔用的CPU利用率

在使用JVisualVM的時候,發現它可以檢視當前JAVA 程序佔用的CPU利用率和GC 所佔用的CPU利用率,很奇怪它是如何計算的或者怎麼獲取的.
本文會根據JVisualVM的原始碼來描述JVisualVM是如何計算的.

獲取原始碼

目標資料

執行jvisualvm, 目標是獲取圖中的CPU Usage 和 GC activity.
target

HOW TO

將原始碼匯入Intellij後,可以全域性搜尋GC activity,然後可以檢視到:
在Bundle.properties 中:

LBL_Gc_Usage=GC activity

然後檢視這個property的使用地方:

com.sun.tools.visualvm.application.views.monitor.ApplicationMonitorView.CpuViewSupport
...

        private static final String CPU = NbBundle.getMessage(ApplicationMonitorView.class, "LBL_Cpu"); // NOI18N
        private static final String CPU_USAGE = NbBundle.getMessage
(ApplicationMonitorView.class, "LBL_Cpu_Usage"); // NOI18N private static final String GC_USAGE = NbBundle.getMessage(ApplicationMonitorView.class, "LBL_Gc_Usage"); // NOI18N ... private void initModels(ApplicationMonitorModel model) { liveModel = model.isLive(); processorsCount = model.getProcessorsCount
(); cpuMonitoringSupported = model.isCpuMonitoringSupported(); gcMonitoringSupported = model.isGcMonitoringSupported(); SimpleXYChartDescriptor chartDescriptor = //下面的這個後面會講到 SimpleXYChartDescriptor.percent(false, 0.1d, model.getChartCache()); chartDescriptor.addLineItems(CPU_USAGE, GC_USAGE); chartDescriptor.setDetailsItems(new String[] { CPU_USAGE, GC_USAGE }); chartSupport = ChartFactory.createSimpleXYChart(chartDescriptor); //這裡就應該就是介面顯示的註冊了. 那麼註冊後是如何使用的??? model.registerCpuChartSupport(chartSupport); chartSupport.setZoomingEnabled(!liveModel); }

主要的計算邏輯集中在其refresh方法中:

public void refresh(ApplicationMonitorModel model) {
     //如果監控CPU
            if (cpuMonitoringSupported || gcMonitoringSupported) {
     //獲取當前的UPTIME
                long upTime = model.getUpTime() * 1000000;
                //獲取上次的UPTIME
                long prevUpTime = model.getPrevUpTime() * 1000000;

                boolean tracksProcessCpuTime = cpuMonitoringSupported &&
                                               model.getPrevProcessCpuTime() != -1;
                                               //獲取當前的CPU時間
                long processCpuTime = tracksProcessCpuTime ?
                    model.getProcessCpuTime() / processorsCount : -1;
                    //獲取上次的CPU時間
                long prevProcessCpuTime = tracksProcessCpuTime ?
                    model.getPrevProcessCpuTime() / processorsCount : -1;

                boolean tracksProcessGcTime  = gcMonitoringSupported &&
                                               model.getPrevProcessGcTime() != -1;
                                               //獲取當前的GC佔用時間
                long processGcTime  = tracksProcessGcTime  ?
                    model.getProcessGcTime() * 1000000 / processorsCount : -1;
                    //獲取上次的GC佔用時間
                long prevProcessGcTime  = tracksProcessGcTime  ?
                    model.getPrevProcessGcTime() * 1000000 / processorsCount : -1;

                if (prevUpTime != -1 && (tracksProcessCpuTime || tracksProcessGcTime)) {

                    long upTimeDiff = upTime - prevUpTime;
                    //分別計算CPU使用率和GC的CPU使用率.
                    long cpuUsage = -1;
                    long gcUsage = -1;
                    String cpuDetail = UNKNOWN;
                    String gcDetail = UNKNOWN;

                    if (tracksProcessCpuTime) {
                        long processTimeDiff = processCpuTime - prevProcessCpuTime;
                        cpuUsage = upTimeDiff > 0 ? Math.min((long)(1000 * (float)processTimeDiff /
                                                             (float)upTimeDiff), 1000) : 0;
                        cpuDetail = cpuUsage == -1 ? UNKNOWN : chartSupport.formatPercent(cpuUsage);
                    }

                    if (tracksProcessGcTime) {
                        long processGcTimeDiff = processGcTime - prevProcessGcTime;
                        gcUsage = upTimeDiff > 0 ? Math.min((long)(1000 * (float)processGcTimeDiff /
                                                            (float)upTimeDiff), 1000) : 0;
                        if (cpuUsage != -1 && cpuUsage < gcUsage) gcUsage = cpuUsage;
                        gcDetail = gcUsage == -1 ? UNKNOWN : chartSupport.formatPercent(gcUsage);
                    }

                    if (liveModel)
                        //設定檢視的值 達到重新整理的目的
                        //可以看到這個值和介面上顯示是相差0.1倍的 原因是前面的那個chartDescriptor 有個0.1d的顯示倍率.
                        chartSupport.addValues(model.getTimestamp(), new long[] { Math.max(cpuUsage, 0), Math.max(gcUsage, 0) });
                    chartSupport.updateDetails(new String[] { cpuDetail, gcDetail });

                }
            }
        }

可以看到邏輯主要是ApplicationMonitorModel model物件的相關方法中,下面介紹這些時間是如何獲取的:
ApplicationMonitorModel的方法:
private void updateValues(final long time, final MonitoredData data)實現了簡單的值更新.主要就是prevXXX = xxx

private void updateValues(final long time, final MonitoredData data) {
        timestamp = time;
        if (data != null) {
            prevUpTime = uptime;
            uptime = data.getUpTime();
            ....
            其他類似

具體實現就在:MonitoredData中.
getUpTime:

getUpTime獲取upTime:
RuntimeMXBean.getUpTime
獲取CPU時間:
/*
JMXPath _processCpuTimeJMXPath = new JMXPath("java.lang:type=OperatingSystem:ProcessCpuTime");
獲取這個JMXPath的值就可以
*/

if (jmxSupport.hasProcessCPUTimeAttribute()) {
            processCpuTime = jmxSupport.getProcessCPUTime(); // 與
        }
獲取GC時間: 累加的GC時間
 //   gcList = ManagementFactory.getGarbageCollectorMXBeans();        
        if (gcList != null && !gcList.isEmpty()) {
            for (GarbageCollectorMXBean gcBean : gcList) {
                collectionTime+=gcBean.getCollectionTime();
            }
        }