我正在尝试使用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多个线程),但我存储了每个线程的所有信息,因此不应该来自不同线程的调用之间的任何干扰。
答案 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时间本身就是纳米级。阻塞和等待时间应该被转换,因为它们是毫秒。它并不完美,而且肯定不是很严格,但它可能会给你更好的数字。
这是我运行的一些测试代码,用于计算我的平均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和高上下文切换。