【问题标题】:Inspecting Java threads in Linux using top使用 top 检查 Linux 中的 Java 线程
【发布时间】:2011-06-13 10:41:00
【问题描述】:

我正在使用检查 Linux 中的 Java 进程

top -H

但是,我无法读取“COMMAND”列中的线程名称(因为它太长了)。如果我使用'c'来扩展进程的全名,那么它仍然太长了。

如何获取命令的全名?

【问题讨论】:

  • 会顶给java线程信息吗??
  • 不是线程而是进程。
  • @OscarRyz, -H 告诉顶部列出(本机)线程。
  • @nhnb 既然如此,我会撤回我的答案
  • 在我的机器顶部 -H 给出了 Java 进程中所有线程的 pid,“COMMAND”列给出了线程的名称。

标签: java linux top-command


【解决方案1】:

您可以使用工具jstack 检查java 线程。它将列出属于指定进程 pid 的所有线程的名称、堆栈跟踪和其他有用信息。

编辑:jstack的thread dump中的参数nid是16进制版本的LWP,显示在线程的pid列中。

【讨论】:

  • 我认为他的意思正是他所写的。他甚至使用 -H 作为 "top" 的参数来告诉 "top" 显示线程。
  • 您可能需要sudo在用户执行java进程的情况下执行命令。
【解决方案2】:

这可能有点老了,但这是我将 top 和 jstack 合并在一起的方法。我使用了两个脚本,但我确信这一切都可以在一个脚本中完成。

首先,我将带有我的 java 线程的 pid 的 top 输出保存到一个文件中,并将 jstack 输出保存到另一个文件中:

#!/bin/sh
top -H -b -n 1 | grep java > /tmp/top.log
jstack -l `ps fax | grep java | grep tomcat | sed "s/ *\([0-9]*\) .*/\1/g"` > /tmp/jstack.log

然后我使用 perl 脚本来调用 bash 脚本(这里称为 cpu-java.sh)并将两个文件(/tmp/top.log 和 /tmp/jstack.log)合并:

#!/usr/bin/perl
system("sh cpu-java.sh");
open LOG, "/tmp/top.log" or die $!;
print "PID\tCPU\tMem\tJStack Info\n";
while ($l = <LOG>) {
    $pid = $l;
    $pid =~ s/root.*//g;
    $pid =~ s/ *//g;
    $hex_pid = sprintf("%#x", $pid);
    @values = split(/\s{2,}/, $l);
    $pct = $values[4];
    $mem = $values[5];
    open JSTACK, "/tmp/jstack.log" or die $!;   
    while ($j = <JSTACK>){
        if ($j =~ /.*nid=.*/){
            if ($j =~ /.*$hex_pid.*/){
                $j =~ s/\n//;
                $pid =~ s/\n//;
                print $pid . "\t" . $pct . "\t" . $mem . "\t" .  $j . "\n";
            }
        }
    }   
    close JSTACK;
}
close LOG;

输出帮助我找出哪些线程占用了我的 CPU:

PID     CPU Mem JStack Info
22460   0   8.0 "main" prio=10 tid=0x083cb800 nid=0x57bc runnable [0xb6acc000]
22461   0   8.0 "GC task thread#0 (ParallelGC)" prio=10 tid=0x083d2c00 nid=0x57bd runnable 
22462   0   8.0 "GC task thread#1 (ParallelGC)" prio=10 tid=0x083d4000 nid=0x57be runnable 
22463   0   8.0 "GC task thread#2 (ParallelGC)" prio=10 tid=0x083d5800 nid=0x57bf runnable 
22464   0   8.0 "GC task thread#3 (ParallelGC)" prio=10 tid=0x083d7000 nid=0x57c0 runnable
...

然后我可以返回 /tmp/jstack.log 并查看有问题线程的堆栈跟踪,并尝试从那里找出发生了什么。当然,这个解决方案是依赖于平台的,但它应该适用于大多数 *nix 风格,并在各处进行一些调整。

【讨论】:

【解决方案3】:

我创建了一个类似于 top 的命令,专门用于可视化按 CPU 使用率排序的 Java 线程,并将源代码发布在:https://github.com/jasta/jprocps。命令行语法不如 top 丰富,但它确实支持一些相同的命令:

$ jtop -n 1

示例输出(显示 ant 和 IntelliJ 正在运行):

  PID   TID USER       %CPU  %MEM  THREAD
13480 13483 jasta      104   2.3   main
13480 13497 jasta      86.3  2.3   C2 CompilerThread1
13480 13496 jasta      83.0  2.3   C2 CompilerThread0
 4866  4953 jasta      1.0   13.4  AWT-EventQueue-1 12.1.4#IC-129.713, eap:false
 4866 14154 jasta      0.9   13.4  ApplicationImpl pooled thread 36
 4866  5219 jasta      0.8   13.4  JobScheduler pool 5/8

从此输出中,我可以手动调出jconsolejstack 中的线程堆栈跟踪,并找出发生了什么。

注意:jtop 是用 Python 编写的,需要安装 jstack

【讨论】:

  • 我得到...“来自 argparse import ArgumentParser ImportError: No module named argparse”
  • 只是一次又一次地打印PID TID USER %CPU %MEM THREAD
【解决方案4】:

据我发现 jstack is outdated 从 JDK 8 开始。我用来检索所有 Java 线程名称的是:

<JDK_HOME>/bin/jcmd <PID> Thread.print

查看jcmd documentation了解更多信息。

【讨论】:

  • 如果众所周知的 tmp 文件 .java_pidnnnn 已经存在于这个 java 进程中,那么你需要在用户执行 java 进程的情况下运行 jcmd,如果进程需要它可能会更好也可以访问此文件。
【解决方案5】:

在 Linux 上使用 OpenJDK,JavaThread 名称不会传播到 native threads,您在使用任何工具检查本机线程时看不到 java 线程名称。

不过还有一些工作正在进行中:

我个人觉得 OpenJDK 开发工具很慢,所以我自己打补丁。

【讨论】:

  • 注:它现在适用于最近的 JDK(包括最近的 openjdk8)
【解决方案6】:

就内核而言,线程没有名称;他们只有身份证号码。 JVM 为线程分配名称,但这是进程内的私有内部数据,“顶级”程序无法访问(而且也不知道)。

【讨论】:

    【解决方案7】:

    这个 shell 脚本结合了 jstack 和 top 的输出来按 CPU 使用率列出 Java 线程。它需要一个参数,即拥有进程的帐户用户。

    名称: jstack-top.sh

    #!/bin/sh
    #
    # jstack-top - join jstack and top to show cpu usage, etc.
    #
    # Usage: jstack-top <user> | view -
    #
    
    USER=$1
    TOPS="/tmp/jstack-top-1.log"
    JSKS="/tmp/jstack-top-2.log"
    
    PIDS="$(ps -u ${USER} --no-headers -o pid:1,cmd:1 | grep 'bin/java' | grep -v 'grep' | cut -d' ' -f1)"
    if [ -f ${JSKS} ]; then
        rm ${JSKS}
    fi
    for PID in ${PIDS}; do
        jstack -l ${PID} | grep "nid=" >>${JSKS}
    done
    
    top -u ${USER} -H -b -n 1 | grep "%CPU\|java" | sed -e 's/[[:space:]]*$//' > ${TOPS}
    while IFS= read -r TOP; do
        NID=$(echo "${TOP}" | sed -e 's/^[[:space:]]*//' | cut -d' ' -f1)
        if [ "${NID}" = "PID" ]; then
            JSK=""
            TOP="${TOP} JSTACK"
        else
            NID=$(printf 'nid=0x%x' ${NID})
            JSK=$(grep "${NID} " ${JSKS})
        fi
        echo "${TOP}    ${JSK}"
    done < "${TOPS}"
    

    【讨论】:

      【解决方案8】:

      在 Perl 中的 Andre's 早期答案的基础上扩展,这是 Python 中运行速度明显更快的答案。

      它会重复使用之前创建的文件,并且不会在 jstack 输出上循环多次:

      #!/usr/bin/env python
      import re
      import sys
      import os.path
      import subprocess
      
      # Check if jstack.log top.log files are present
      if not os.path.exists("jstack.log") or not os.path.exists("top.log"):
        # Delete either file
        os.remove("jstack.log") if os.path.exists("jstack.log") else None
        os.remove("top.log") if os.path.exists("top.log") else None
        # And dump them via a bash run
        cmd = """
        pid=$(ps -e | grep java | sed 's/^[ ]*//g' | cut -d ' ' -f 1)
        top -H -b -n 1 | grep java > top.log
        /usr/intel/pkgs/java/1.8.0.141/bin/jstack -l $pid > jstack.log
        """
        subprocess.call(["bash", "-c", cmd])
      
      # Verify that both files were written
      for f in ["jstack.log", "top.log"]:
        if not os.path.exists(f):
          print "ERROR: Failed to create file %s" % f
          sys.exit(1)
      
      # Thread ID parser
      jsReg = re.compile('"([^\"]*)".*nid=(0x[0-9a-f]*)')
      # Top line parser
      topReg = re.compile('^\s*([0-9]*)(\s+[^\s]*){7}\s+([0-9]+)')
      
      # Scan the entire jstack file for matches and put them into a dict
      nids = {}
      with open("jstack.log", "r") as jstack:
        matches = (jsReg.search(l) for l in jstack if "nid=0x" in l)
        for m in matches:
          nids[m.group(2)] = m.group(1)
      
      # Print header
      print "PID\tNID\tCPU\tTHREAD"
      # Scan the top output and emit the matches
      with open("top.log", "r") as top:
        matches = (topReg.search(l) for l in top)
        for m in matches:
          # Grab the pid, convert to hex and fetch from NIDS
          pid = int(m.group(1))
          nid = "0x%x" % pid
          tname = nids.get(nid, "<MISSING THREAD>")
          # Grab CPU percent
          pct = int(m.group(3))
          # Emit line
          print "%d\t%s\t%d\t%s" % (pid, nid, pct, tname)
      

      【讨论】:

        【解决方案9】:

        老问题,但我在top 上遇到了同样的问题。

        事实证明,您只需使用光标键就可以滚动顶部的输出到右侧:)

        (但不幸的是,不会显示任何线程名称

        【讨论】:

          【解决方案10】:

          您提到了“Linux”。那么使用小工具“threadcpu”可能是一个解决方案:

          threadcpu_-_show_cpu_usage_of_threads

          $ threadcpu -h
          
          threadcpu shows CPU usage of threads in user% and system%
          
          usage:
          threadcpu [-h] [-s seconds] [-p path-to-jstack]
          
          options:
            -h display this help page
            -s measuring interval in seconds, default: 10
            -p path to JRE jstack, default: /usr/bin/jstack
          example usage:
            threadcpu -s 30 -p /opt/java/bin/jstack 2>/dev/null|sort -n|tail -n 12
          output columns:
            user percent <SPACE> system percent <SPACE> PID/NID [ <SPACE> JVM thread name OR (process name) ]
          

          一些示例输出:

          $ threadcpu |sort -n|tail -n 8
          3 0 33113 (klzagent)
          3 0 38518 (klzagent)
          3 0 9874 (BESClient)
          3 41 6809 (threadcpu)
          3 8 27353 VM Periodic Task Thread
          6 0 31913 hybrisHTTP4
          21 8 27347 C2 CompilerThread0
          50 41 3244 (BESClient)
          
          $ threadcpu |sort -n|tail -n 8
          0 20 52358 (threadcpu)
          0 40 32 (kswapd0)
          2 50 2863 (BESClient)
          11 0 31861 Gang worker#0 (Parallel CMS Threads)
          11 0 31862 Gang worker#1 (Parallel CMS Threads)
          11 0 31863 Gang worker#2 (Parallel CMS Threads)
          11 0 31864 Gang worker#3 (Parallel CMS Threads)
          47 10 31865 Concurrent Mark-Sweep GC Thread
          
          $ threadcpu |sort -n|tail -n 8
          2 0 14311 hybrisHTTP33
          2 4 60077 ajp-bio-8009-exec-11609
          2 8 30657 (klzagent)
          4 0 5661 ajp-bio-8009-exec-11649
          11 16 28144 (batchman)
          15 20 3485 (BESClient)
          21 0 7652 ajp-bio-8009-exec-11655
          25 0 7611 ajp-bio-8009-exec-11654
          

          有意使输出非常简单,以便进一步处理(例如用于监控)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-02
            • 2012-12-15
            • 2017-09-09
            相关资源
            最近更新 更多