天天看點

性能測試中擷取JVM資源資訊

作者:FunTester

在以往性能測試中,通常施壓機的硬體資源不會成為壓力瓶頸,但是在多任務并行的場景中,如果一個任務占用目前機器資源過多,會影響其他任務執行。或者目前用例本身存在問題,導緻性能無法進一步提升,影響了性能測試執行。

根據以上場景,如果能從監控工程上得到解決自然是最好的。可以實時監控施壓機和施壓程序的CPU占用、記憶體使用、GC清空。但是,重點來了,并不是總能擁有一套完美的監控系統。這個時候,就需要自己手動解決一些痛點。

經過查閱資源,最終将方案鎖定在java.lang.management.ManagementFactory這個類,看名字和路徑大概能猜個七七八八了。以上我提到的資訊都可以調用這個類的API擷取。

CPU使用率

下面分享一下如何擷取目前JVM的CPU使用情況。

static ThreadMXBean threadBean = ManagementFactory.getThreadMXBean()

    static OperatingSystemMXBean osMxBean = ManagementFactory.getOperatingSystemMXBean()

    static long lastSysTime = System.nanoTime()

    static long lastUserTime = 0
    
    /**
     * 擷取最大程序數N,CPU使用率N*100%
     * @return
     */
    static int getAvailableProcessors() {
        osMxBean.getAvailableProcessors()
    }

    /**
     * 擷取目前線程CPU使用率,最大100
     * 已乘以100,已經除以了系統最大程序數
     * @return
     */
    static double getCpuUsage(boolean avg = true) {
        long totalTime = 0
        for (long id : threadBean.getAllThreadIds()) {
            totalTime += threadBean.getThreadCpuTime(id)
        }
        long curtime = System.nanoTime()
        long usedTime = totalTime - lastUserTime
        long totalPassedTime = curtime - lastSysTime
        lastSysTime = curtime
        lastUserTime = totalTime
        def d = avg ? (((double) usedTime) / totalPassedTime / getAvailableProcessors()) * 100 : (((double) usedTime) / totalPassedTime) * 100
        return d > 100 ? 8.88 : d
    }

           

這裡我用了一個參數,用來區分是否傳回平均使用率還是傳回總使用率之和。因為在docker環境中com.funtester.utils.OSUtil#getAvailableProcessors傳回值着實讓我很迷惑,至今還沒懂其中奧妙。

下面分享擷取系統負載的方法:

/**
     * 擷取系統一分鐘内的平均load
     * @return
     */
    static def getLoad() {
        osMxBean.getSystemLoadAverage() / getAvailableProcessors()
    }
           

同樣的問題也存在這個方法中,但目前使用比較少,就沒有做修改。

擷取GC資訊

static List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();

    /**
     * 擷取GC資訊{@link com.funtester.utils.OSUtil.GCInfo}
     * @return
     */
    static def getGCinfo() {
        def infos = []
        for (GarbageCollectorMXBean gcMxBean : gcMxBeans) {
            infos << new GCInfo(gcMxBean)
        }
        infos
    }

    /**
     * GC資訊類
     */
    static class GCInfo extends AbstractBean {

        String name

        int count

        int time

        GCInfo(String name, int count, int time) {
            this.name = name
            this.count = count
            this.time = time
        }

        GCInfo(GarbageCollectorMXBean gcMxBean) {
            this.name = gcMxBean.getName()
            this.count = gcMxBean.getCollectionCount()
            this.time = gcMxBean.getCollectionTime()
        }

    }
           

這裡隻能算是個輸出,很少用GC資訊作為獨立的依據。

其他

擷取記憶體資訊:

/**
     * 擷取堆記憶體資訊
     * @return
     */
    static def heapMemInfo() {
        memoryMXBean.getHeapMemoryUsage()
    }

    /**
     * 擷取非堆記憶體資訊
     * @return
     */
    static def noHeapMemInfo() {
        memoryMXBean.getNonHeapMemoryUsage()
    }
           

使用場景

目前我的使用場景主要2個:

  1. 在本地執行性能測試場景中,将JVM資訊定期輸出,包含在性能測試資料取樣的功能中。
  2. 在服務執行性能測試場景中,将JVM資訊作為一個資源調配的名額。例如:CPU資源占用過高,就降低一下對象池的活躍資料,主動回收一些資源。

在查閱資料的過程中,發現SDK的API很少能夠直接擷取硬體資訊的,很多案例都是通過com.github.oshi:oshi-core:6.4.0這個類庫實作的。看了一下文檔,功能非常強大,有興趣的可以直接上這個。