awk
awk

Reputation: 526

Monitor cpu usage per thread in java?

I would like to ask whether there is some simple way to determine cpu usage per thread in java. Thanks

Upvotes: 36

Views: 41996

Answers (7)

99Sono
99Sono

Reputation: 3687

My answer to this goes in a different direction.

A a very powerful and transparent way, and also super for production of collection thread CPU statistics is not by having your java program collect it internally, but by having an programing language agnostic approach to do it.

In linux for example, you have the TOP command that has options to collect CPU statistics focused a single java process.

top -bcH -n 1 -p ${theProcesIdYouWantToMonitor} 

See: https://man7.org/linux/man-pages/man1/top.1.html

-H, --threads-show Instructs top to display individual threads. Without this command-line option a summation of all threads in each process is shown. Later this can be changed with the `H' interactive command.

Does not get any simpler than that.

If you then use jstack in parallel with a process that monitors CPU utilization per thread then you have the best of both worlds: (a) You know at a given point in time what your code was doing via jstack thread dumps. But this only one part of the picture. This does not tell you who is actually burning your cpu, just what is happening. If for example your CPU is being burned by garbage colleciton threads you will never know that out of the thread dump.

(b) You pair the jstack time stamp with that of a tool like linux TOP, as shown above.

You need to convert the linux thread id into a an HEX decimal. And the hex decimal appears in our stack trace. And there you have it.

Now you know at any point in time that you have sampled, who was burning your cpu and what it was doing. Finally, your strange CPU spiles are explained.

For windows, I have just discovered recently how to do the same. Here is the reference: https://xkln.net/blog/analyzing-thread-cpu-utilization-with-processexplorer-powershell-and-wmi/

So here is some code that could be useful to you:

# use the command:
#     Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
# in order to be allowed to run the powerhsell script

# SEE Original script: https://xkln.net/blog/analyzing-thread-cpu-utilization-with-processexplorer-powershell-and-wmi/

# (a) Discover the process we want to be monitoring
$process = ( (Get-WmiObject win32_process -Filter "name like '%java%'") | Where-object  {$_.CommandLine -like "*power*"} )
$processId = $process.processId

# (b) define the folder where we want to write out our staticis 
$baseFolterForSamples="C:\dev\apps_standalone\powersheell_threadCpuUtilization\threadCpuSamples"

# (c) Definition of a function that loops forever collecting the thread id of a 

    process
    function Get-ThreadProcessorUtilization() {
    
        Param(
            [Parameter(Mandatory=$true)]
            [int]$ProcId,
            [int]$DurationMs = 2000
        )
    
        $StatsPrev=$null;
        $ThreadAge = @{L="ThreadAge";E={[Timespan]::FromMilliseconds($_.ElapsedTime).ToString("hh\:mm\:ss")}}
        $ProcUtilization = @{L="ProcUtilization";E={(([Math]::Round(($_.UserModeTime - ($StatsPrev | ? Handle -eq $_.Handle).UserModeTime)/($_.ElapsedTime - ($StatsPrev | ? Handle -eq $_.Handle).ElapsedTime)*100,2)).ToString() + "%")}}
        
        # Define dome helper loop variables
        $currentItrationNumber = 0
        $previousIterationDateTime = $null
        
        while ($true) {
            $Stopwatch =  [System.Diagnostics.Stopwatch]::StartNew(); 
            $Stats = Get-CimInstance Win32_Thread -filter "ProcessHandle = $ProcId" | Select ProcessHandle, Handle, UserModeTime, ElapsedTime | Sort-Object -Property UserModeTime -Descending; 
            
            # create some loop variables that we will use to define the file name into which we want to 
            $currentTirationDateTime = $(get-date -f yyyy-MM-dd'T'hh:mm:ss.fff)
            $currentItrationNumberLpadZeros = '{0:d6}' -f $currentItrationNumber
            $currentOutputFileName = "$baseFolterForSamples\ThreadCpuTime_PID_${ProcId}_ItrationNr_$currentItrationNumber.txt"
            
            
            if ($StatsPrev -ne $null) {
                # This is the slow statement that is collecting the stats
                $CurrStats = $Stats | Select-Object ProcessHandle, Handle, $ThreadAge, $ProcUtilization
                Clear-Host
                
                # Write the current statistics into an output file
                $fileHeaderStart = "START Current Itration:  $currentItrationNumber Time Interval Sampled: [$previousIterationDateTime until $currentTirationDateTime]: "
                $fileHeaderEnd = "END Current Itration:  $currentItrationNumber Time Interval Sampled: [$previousIterationDateTime until $currentTirationDateTime]: "
                Write-Output $fileHeaderStart | Out-File -Filepath $currentOutputFileName
                Write-Output $CurrStats | Format-Table -AutoSize | Out-File -Filepath $currentOutputFileName -Append 
                Write-Output $fileHeaderEnd | Out-File -Filepath $currentOutputFileName -Append
                
                # REM Explain what we have just done here:
                echo "Generated sample output file: $currentOutputFileName with stats for CPU thread utilization for time period: [$previousIterationDateTime until $currentTirationDateTime]"
            }
            $statsPrev=$stats
            $Stopwatch.Stop()
    
            # This value could be coming negative          
            # $sleepDuration = $DurationMs-($Stopwatch.ElapsedMilliseconds)
            $sleepDuration = $DurationMs-($Stopwatch.ElapsedMilliseconds)
            $sleepDuration = [Math]::Max($sleepDuration,0)
            [System.Threading.Thread]::Sleep( $sleepDuration )
            
            # update the counter of curernt iteration
            $currentItrationNumber =  $currentItrationNumber + 1
            $previousIterationDateTime = $currentTirationDateTime
        }
    }
    
    Get-ThreadProcessorUtilization -ProcId $processId

You will see appearing in the folder: C:\dev\pps_standalone\powersheell_threadCpuUtilization\threadCpuSamples

The code you see thre is not exactly the same as the one in the reference: https://xkln.net/blog/analyzing-thread-cpu-utilization-with-processexplorer-powershell-and-wmi/

But it is close enough that you just need to read the reference to understand it.

And this, in my opinion, I was better approach because you apply it to any java project that you have ecounter along the way and manifests CPU spikes performance problems. You do not a massive infrastructure to do a super high quality analysis. Jstack + Thread Per Cpu is all you need.

If you wish, of course, you can integrate into your big data server elastic server / or whatever by reading the produced files of jstacks together with thread statistics and pumping them to your big data storage.

Well, the sky is the limit really.

Unfortunately, the graphical user interfaces like the task manager, they are nice to show you here is a spike ... something is somehow not going well. But you will not derive much more than that. You then need to come down to worth and point your finger: -> in that interval of time it was that thread that was gobling 50% cpu.

For linux the solution is way similar, you can just google how to get CPU per thread of a process with top command, and there you have the keys to to the ferrari.

So now you know how to do it properly.

Upvotes: 0

VonC
VonC

Reputation: 1329082

I believe the JConsole (archived link) does provide this kind of information through a plugin

JConsole Thread CPU usage, after blogs.oracle.com/lmalventosa/resource/thread_cpu_usage.jpg

It uses ThreadMXBean getThreadCpuTime() function.

Something along the line of:

        long upTime = runtimeProxy.getUptime();
        List<Long> threadCpuTime = new ArrayList<Long>();
        for (int i = 0; i < threadIds.size(); i++) {
            long threadId = threadIds.get(i);
            if (threadId != -1) {
                threadCpuTime.add(threadProxy.getThreadCpuTime(threadId));
            } else {
                threadCpuTime.add(0L);
            }
        }
        int nCPUs = osProxy.getAvailableProcessors();
        List<Float> cpuUsageList = new ArrayList<Float>();
        if (prevUpTime > 0L && upTime > prevUpTime) {
            // elapsedTime is in ms
            long elapsedTime = upTime - prevUpTime;
            for (int i = 0; i < threadIds.size(); i++) {
                // elapsedCpu is in ns
                long elapsedCpu = threadCpuTime.get(i) - prevThreadCpuTime.get(i);
                // cpuUsage could go higher than 100% because elapsedTime
                // and elapsedCpu are not fetched simultaneously. Limit to
                // 99% to avoid Chart showing a scale from 0% to 200%.
                float cpuUsage = Math.min(99F, elapsedCpu / (elapsedTime * 1000000F * nCPUs));
                cpuUsageList.add(cpuUsage);
            }
        }

Upvotes: 25

Kanagavelu Sugumar
Kanagavelu Sugumar

Reputation: 19270

Option_1: Code level

In your business logic code; in the beginning call start() API and in the finally block call stop(). So that you will get CPU time for executing your logic by the current running thread. Then log it. Reference.

class CPUTimer 
{
    private long _startTime = 0l;

    public void start ()
    {
        _startTime = getCpuTimeInMillis();
    }


    public long stop ()
    {
        long result = (getCpuTimeInMillis() - _startTime);
        _startTime = 0l;
        return result;
    }

    public boolean isRunning ()
    {
        return _startTime != 0l;
    }

    /** thread CPU time in milliseconds. */
    private long getCpuTimeInMillis ()
    {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        return bean.isCurrentThreadCpuTimeSupported() ? bean.getCurrentThreadCpuTime()/1000000: 0L;
    }
}

Option_2: Monitor level using plugins (AIX IBM box which don't have jvisualvm support)

If you think it is delay in adding code now, then you can prefer JConsole with plugins support. I followed this article. Download the topthreads jar from that article and run ./jconsole -pluginpath topthreads-1.1.jar

Option_3: Monitor level using TOP (shift H) + JSTACK (Unix machine which has 'Shif+H' support)

Follow this tutorial, where top command will give option to find top CPU thread (nid). Take that check that nid in jstack output file.

Upvotes: 7

peter
peter

Reputation: 21

Try the "TopThreads" JConsole plugin. See http://lsd.luminis.nl/top-threads-plugin-for-jconsole/

Upvotes: 2

idrosid
idrosid

Reputation: 8029

Indeed the object ThreadMXBean provides the functionality you need (however it might not be implemented on all virtual machines).

In JDK 1.5 there was a demo program doing exactly what you need. It was in the folder demo/management and it was called JTop.java

Unfortnately, it's not there in Java6. Maybe you can find at with google or download JDK5.

Upvotes: 1

dfa
dfa

Reputation: 116442

by using java.lang.management.ThreadMXBean. How to obtain a ThreadMXBean:

 ThreadMXBean tmxb = ManagementFactory.getThreadMXBean();

then you can query how much a specific thread is consuming by using:

 long cpuTime = tmxb.getThreadCpuTime(aThreadID);

Hope it helps.

Upvotes: 15

Gadi
Gadi

Reputation: 482

Though this is platform dependent, I believe what you're looking for is the ThreadMXBean: http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/ThreadMXBean.html . You can use the getThreadUserTime method, for example, to get what you need. To check if your platform supports CPU measurement, you can call isThreadCpuTimeSupported() .

Upvotes: 1

Related Questions