Obtaining CPU thread usage in Java

2019-02-09 06:19发布

问题:

I have a question around getting CPU utilization for a given JNI block. I'm making some intensive CPU computation in the underlying C++ JNI native method. I'm in the process of optimizing this computation and want to benchmark it against varying inputs.

I need some guidance on how to go about measuring this. The alternatives I have considered so far are

  • Using JMX ThreadMXBean to measure system CPU usage for the current thread that invokes call into JNI method. However, I am not sure if JNI code is executed within the invoking thread context. What happens when the thread spawns more threads?

  • Using JMX OperatingSystemMXBean to get the CPU usage for the entire JVM. Ideally, this is not want I want as there could be parallel executions in JVM that might tweak the benchmarking.

  • Measure externally using getrusage(..). What I want to know here is that how is it different than using OperatingSystemMXBean.

回答1:

You can use ThreadMXBean to get cpu usage statistics from all running threads. In the example below the CPU usage per thread is calculated:

private int sampleTime = 10000;
private ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
private RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
private OperatingSystemMXBean osMxBean = ManagementFactory.getOperatingSystemMXBean();
private Map<Long, Long> threadInitialCPU = new HashMap<Long, Long>();
private Map<Long, Float> threadCPUUsage = new HashMap<Long, Float>();
private long initialUptime = runtimeMxBean.getUptime();

ThreadInfo[] threadInfos = threadMxBean.dumpAllThreads(false, false);
for (ThreadInfo info : threadInfos) {
    threadInitialCPU.put(info.getThreadId(), threadMxBean.getThreadCpuTime(info.getThreadId()));
}

try {Thread.sleep(sampleTime);} catch (InterruptedException e) {}

long upTime = runtimeMxBean.getUptime();

Map<Long, Long> threadCurrentCPU = new HashMap<Long, Long>();
ThreadInfo[] threadInfos = threadMxBean.dumpAllThreads(false, false);
for (ThreadInfo info : threadInfos) {
    threadCurrentCPU.put(info.getThreadId(), threadMxBean.getThreadCpuTime(info.getThreadId()));
}

// CPU over all processes
//int nrCPUs = osMxBean.getAvailableProcessors();
// total CPU: CPU % can be more than 100% (devided over multiple cpus)
long nrCPUs = 1;
// elapsedTime is in ms.
long elapsedTime = (upTime - initialUptime);
for (ThreadInfo info : threadInfos) {
    // elapsedCpu is in ns
    Long initialCPU = threadInitialCPU.get(info.getThreadId());
    if (initialCPU != null) {
        long elapsedCpu = threadCurrentCPU.get(info.getThreadId()) - initialCPU;
        float cpuUsage = elapsedCpu / (elapsedTime * 1000000F * nrCPUs);
        threadCPUUsage.put(info.getThreadId(), cpuUsage);
    }
}

// threadCPUUsage contains cpu % per thread
System.out.println(threadCPUUsage);
// You can use osMxBean.getThreadInfo(theadId) to get information on every thread reported in threadCPUUsage and analyze the most CPU intentive threads


回答2:

On Linux, this method may help you:

  • Run kill -3 <pid of Java process> to get the thread dump. Thread dump will be added standard output.
  • Open the dump and search for the thread you're interested in. (You can search using class name)
  • Get the thread PID (it's in hex) and convert it to decimal.
  • Run top command.
  • Press Shift-H to enable Threads View
  • Identify the thread using decimal value you obtained above. You can see cpu / memory usage of your thread there.