web-dev-qa-db-fra.com

Inspection des threads Java sous Linux avec top

J'inspecte un processus Java sous Linux en utilisant

top -H

Cependant, je ne peux pas lire le nom du fil dans la colonne "COMMAND" (car il est trop long). Si j'utilise 'c' pour développer le nom complet du processus, alors il est encore trop long pour l'adapter.

Comment puis-je obtenir le nom complet de la commande?

33
Jake

C’est peut-être un peu vieux, mais voici ce que j’ai fait pour fusionner un peu haut et jstack ensemble. J'ai utilisé deux scripts, mais je suis sûr que tout pourrait être fait en un.

Premièrement, j'enregistre la sortie de top avec les pids de mes threads Java dans un fichier et enregistre la sortie de jstack dans un autre fichier:

#!/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

Ensuite, j'utilise un script Perl pour appeler le script bash (appelé ici cpu-Java.sh) et un peu fusionner les deux fichiers (/tmp/top.log et /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;

La sortie m'aide à savoir quels threads monopolisent mon processeur:

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
...

Ensuite, je peux revenir à /tmp/jstack.log et jeter un coup d'œil à la trace de pile pour le thread problématique et essayer de comprendre ce qui se passe à partir de là. Bien sûr, cette solution dépend de la plate-forme, mais elle devrait fonctionner avec la plupart des versions de * nix et quelques modifications apportées ici et là.

23
Andre

Vous pouvez inspecter les threads Java avec l'outil jstack. Il listera les noms, stacktraces et autres informations utiles de tous les threads appartenant au pid de processus spécifié.

Edit : Le paramètre nid dans le thread dump de jstack est la version hexadécimale du LWP affichée en haut dans la colonne pid des threads.

23
Hendrik Brummermann

J'ai créé une commande de type top spécifiquement pour visualiser les threads Java classés en fonction de l'utilisation du processeur et posté le code source à l'adresse: https://github.com/jasta/jprocps . La syntaxe de la ligne de commande n’est pas aussi riche que celle de top, mais elle prend en charge certaines des mêmes commandes:

$ jtop -n 1

Exemple de sortie (montrant ant et IntelliJ en cours d'exécution):

  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

À partir de cette sortie, je peux extraire manuellement la trace de pile du thread dans jconsole ou jstack et comprendre ce qui se passe.

NOTE:jtop est écrit en Python et nécessite que jstack soit installé.

14
Josh Guilfoyle

Les threads n'ont pas de noms en ce qui concerne le noyau; ils ont seulement des numéros d'identification. La JVM attribue des noms aux threads, mais il s’agit de données internes privées au sein du processus, auxquelles le programme "top" ne peut pas accéder (et n’a pas connaissance de toute façon).

4
Wyzard

Pour autant que j'ai découvert jstack est obsolète à partir de JDK 8. Ce que j'ai utilisé pour récupérer tous les noms de threads Java est:

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

Vérifiez documentation jcmd pour plus.

2
Trinimon

Avec OpenJDK sous Linux, JavaThread noms ne se propagent pas vers threads natifs , vous ne pouvez pas voir le nom du thread Java lors de l'inspection des threads natifs avec un outil.

Cependant, des travaux sont en cours:

Personnellement, je trouve l'outil de développement OpenJDK lent, aussi je n'applique que moi-même les correctifs.

2
milan

Ce script shell combine les résultats de jstack et top pour répertorier les threads Java en fonction de l'utilisation du processeur. Il attend un argument, l'utilisateur du compte qui possède les processus.

Nom: 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}"
1
Rick O'Sullivan

Vieille question, mais j'avais exactement le même problème avec top.

En fin de compte, vous pouvez faire défiler la sortie du haut vers la droite en utilisant simplement les touches des curseurs :)

(mais malheureusement, il n'y aura pas de fil name montré)

0
Scheintod

En développant Andre's plus tôt en Perl, en voici un en Python qui tourne beaucoup plus vite.

Il réutilise les fichiers créés précédemment et ne boucle pas plusieurs fois sur la sortie 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)
0
Martin Schroeder

Vous avez parlé de "Linux". Ensuite, utiliser le petit outil "threadcpu" pourrait être une solution:

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) ]

Quelques exemples de sorties:

$ 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

La sortie est intentionnellement très simple pour faciliter le traitement ultérieur (par exemple, pour la surveillance).

0
reichhart