JMX:CPU +阻塞+等待时间=待机时间?

时间:2013-06-26 12:30:36

标签: java jmx

我正在尝试使用JMX来测量方法调用花费的时间,以及CPU时间的多少,被阻塞的线程数以及等待的数量。理想情况下,我希望 CPU时间+阻止时间+等待时间=待机时间,但我注意到情况并非如此 - 并且它不仅仅是稍微不准确的计时器。例如:

Wall time:  5657.305 ms
CPU time:   4060.000 ms (71.77%)
User time:  3840.000 ms (67.88%)
Block time: 0.000 ms (0.00%)
Wait time:  0.000 ms (0.00%)

所以,问题是...... 是我的推测这些时间的总和(不是用户时间,包含在CPU时间中)应该给Wall时间错误?我错过了什么吗?

更多细节:

  • 待命时间:方法进入和退出时System.currentTimeMillis()的差异

  • CPU时间:方法进入和退出时ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()的差异

  • 阻止和等待时间:类似于CPU,ManagementFactory.getThreadMXBean().getThreadInfo(Thread.currentThread().getId()).getBlockedTime()getWaitedTime()

  • 是的,我知道这些方法以不同的单位(ns / ms)返回时间,我考虑到了这一点。

  • 应用程序是高度超线程的(4000多个线程),但我存储了每个线程的所有信息,因此不应该来自不同线程的调用之间的任何干扰。

3 个答案:

答案 0 :(得分:2)

CPU时间告诉您线程利用CPU花了多少时间,即实际执行代码。如果由于例如线程被挂起,则阻塞或等待时间增加等待I / O或进入监视器。

但是,由JVM和OS来为线程和进程分配CPU时间。如有必要,他们可以选择暂停线程或进程并随时恢复另一个。因此,线程可能处于既不阻塞也不等待但只是不执行的状态。这将增加挂起时间,但不会阻止/等待时间。

CPU时间值来自操作系统。我检查了Linux系统的OpenJDK 7,因此在其他平台上可能会有所不同。调用fast_thread_cpu_time()slow_thread_cpu_time()来计算线程的CPU时间。这两个函数都位于OpenJDK 7源代码的hotspot/src/os/linux/vm/os_linux.cpp中。

OpenJDK uses pthreads on Linux开始,每个Java线程都实现为轻量级OS线程。现代内核支持的快速方法调用pthread_getcpuclockid来检索特定线程的clock_id,然后调用clock_gettime来检索线程的CPU时间。慢速方法将从/proc/self/task/<tid>/stat查找CPU时间值。这两种方法都告诉您线程在用户和系统/内核空间消耗CPU的时间。

<强>参考文献:

答案 1 :(得分:1)

您可以提高测量精度的另一种方法是:

  • 以纳秒为单位进行计算,因此经过的时间(使用System.nanoTime()和CPU时间本身就是纳米级。阻塞和等待时间应该被转换,因为它们是毫秒。
  • 实现简单捕获已过去,cpu,阻塞和等待时间所需的时间的近似值。 (称之为OVERHEAD。)你可以在某个地方的静态块中执行此操作。首先执行JVM预热循环,记住JIT在15000方法调用后启动[默认情况下?取决于你的JVM ....],所以至少这个大小的预热循环会很好。
  • 运行后暖机循环,将经过的总时间除以循环计数,您将获得OVERHEAD值。
  • 从计算出的方法经过的时间中减去OVERHEAD,以更好地了解方法的代码执行时间,或....
  • 添加OVERHEAD以添加CPU,Blocked和Waited以更接近实际的待机时间。
  • 可选择重新计算OVERHEAD。

它并不完美,而且肯定不是很严格,但它可能会给你更好的数字。

这是我运行的一些测试代码,用于计算我的平均OVERHEAD(Java 7,Windows 7,64位)。 我试图确保没有任何方法被剔除,但你的里程可能会有所不同。

public class Overhead {
    static final ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
    public static void main(String[] args) {

        tmx.setThreadContentionMonitoringEnabled(true);
        tmx.setThreadCpuTimeEnabled(true);
        int loops = 15000;
        long sum = -1;
        long start = System.nanoTime();
        for(int i  = 0; i < loops; i++) {
            sum = measure();
        }
        long elapsed = System.nanoTime()-start;
        log("Warmup completed in [" + elapsed + "] ns. ");
        log("Sum:" + sum);

        start = System.nanoTime();
        loops = loops * 2;
        for(int i  = 0; i < loops; i++) {
            sum = measure();
        }
        elapsed = System.nanoTime()-start;

        long avg = (elapsed/loops);

        log("Test completed in [" + elapsed + "] ns. OVERHEAD: [" + avg + "] ns.");
        log("Sum:" + sum);
    }

    protected static long measure() {
        long s1 = System.nanoTime();
        long bt = tmx.getCurrentThreadCpuTime();
        ThreadInfo ti = tmx.getThreadInfo(Thread.currentThread().getId());
        long blocked = ti.getBlockedTime();
        long waited = ti.getWaitedTime();
        long s2 = System.nanoTime();
        return ((s2 - s1) + blocked + waited + bt);
    }


    public static void log(Object msg) {
        System.out.println(msg);
    }
}

我的输出如下:

Overhead test
Warmup completed in [43176164] ns. 
Sum:109201929
Test completed in [38482368] ns. OVERHEAD: [1282] ns.
Sum:156002228

答案 2 :(得分:0)

从代码执行的角度来看,线程可以在监视器上运行,阻塞或等待io。但是,正在运行的线程必须与其他正在运行的线程竞争才能分配给cpu才能运行 - 直到它被分配给cpu,它实际上是空闲,不占用CPU时间而是占用时间。如果您有1000个线程但只有几个cpu核心,则空闲可能很重要。如果是这种情况,您可能会看到带有'vmstat'的高用户CPU和高上下文切换。