unix-process-thread

Linux下查看 Java/Python 进程线程

先使用 ps -ef | grep [processname] 查看进程id

进程文件proc

查看status文件; cat /proc/[pid]/status;

pstree

pstree -p 7604

java(7604)─┬─{java}(7605)
           ├─{java}(7606)
           ├─{java}(7607)
           ├─{java}(7608)
           ├─{java}(7609)
           ├─{java}(7610)
           ├─{java}(7611)
           ├─{java}(7612)
           ├─{java}(7613)
           ├─{java}(7614)
           ├─{java}(7615)
           ├─{java}(7616)
           ├─{java}(7617)
           ├─{java}(7618)
           ├─{java}(7619)
           ├─{java}(7620)
           ├─{java}(7621)
           ├─{java}(7622)
           ├─{java}(7623)
           ├─{java}(7624)
           ├─{java}(7625)
           └─{java}(7626)

top

ps

ps -eo ruser,pid,ppid,lwp,psr,args -L | grep java

Java 进程线程堆栈分析工具

jps & jstack

jps 一般可以直接用来获取 java 进程 id; 比如我运行了一个8线程的java程序;jps命令格式 jps [ options ] [ hostid ]
主选项:

选 项 作 用
-q 只输出LVMID,省略主类的名称
-m 输出虚拟机进程启动时传递给主类main()函数的参数
-l 输出主类的全名,如果进程执行的是jar包,输出jar包路径
-v 输出虚拟机进程启动时的JVM参数
6993 org.eclipse.equinox.launcher_1.3.200.v20160318-1642.jar
7604 ThreadNums
15721 Jps
1372 Bootstrap

进程号是 7604;

jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的目的主要是定位线程长时间出现停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的原因。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者在等待些什么资源。

sudo jstack [pid] 查看 某个java进程的线程状态. 如果不反应, 使用强制参数 sudo jstack -F [pid];可以直接查到各个线程的状态,运行到哪一句代码等.

Attaching to process ID 7604, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11
Deadlock Detection:

No deadlocks found.

Thread 7626: (state = IN_JAVA)
 - multicore.forkjoin.PrimeNumberConcurrencyAtomic.run() @bci=21, line=20 (Compiled frame; information may be imprecise)


Thread 7625: (state = IN_JAVA)
 - multicore.forkjoin.PrimeNumberConcurrencyAtomic.run() @bci=21, line=20 (Compiled frame; information may be imprecise)


Thread 7624: (state = IN_JAVA)
 - multicore.forkjoin.PrimeNumberConcurrencyAtomic.run() @bci=21, line=20 (Compiled frame; information may be imprecise)


Thread 7623: (state = IN_JAVA)
 - multicore.forkjoin.PrimeNumberConcurrencyAtomic.run() @bci=21, line=20 (Compiled frame; information may be imprecise)


Thread 7622: (state = IN_JAVA)
 - multicore.forkjoin.PrimeNumberConcurrencyAtomic.run() @bci=21, line=20 (Compiled frame; information may be imprecise)


Thread 7621: (state = IN_JAVA)
 - multicore.forkjoin.PrimeNumberConcurrencyAtomic.run() @bci=21, line=20 (Compiled frame; information may be imprecise)


Thread 7620: (state = IN_JAVA)
 - multicore.forkjoin.PrimeNumberConcurrencyAtomic.run() @bci=21, line=20 (Compiled frame; information may be imprecise)


Thread 7619: (state = IN_JAVA)
 - multicore.forkjoin.PrimeNumberConcurrencyAtomic.run() @bci=21, line=20 (Compiled frame; information may be imprecise)


Thread 7613: (state = BLOCKED)


Thread 7612: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.ref.ReferenceQueue.remove(long) @bci=59, line=143 (Interpreted frame)
 - java.lang.ref.ReferenceQueue.remove() @bci=2, line=164 (Interpreted frame)
 - java.lang.ref.Finalizer$FinalizerThread.run() @bci=36, line=209 (Interpreted frame)


Thread 7611: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.Object.wait() @bci=2, line=502 (Interpreted frame)
 - java.lang.ref.Reference.tryHandlePending(boolean) @bci=54, line=191 (Interpreted frame)
 - java.lang.ref.Reference$ReferenceHandler.run() @bci=1, line=153 (Interpreted frame)


Thread 7605: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.Thread.join(long) @bci=38, line=1252 (Interpreted frame)
 - java.lang.Thread.join() @bci=2, line=1326 (Interpreted frame)
 - multicore.forkjoin.PrimeNumberConcurrencyAtomic.primeNumbers(java.util.concurrent.atomic.AtomicLong, int, long) @bci=65, line=45 (Interpreted frame)
 - multicore.forkjoin.ThreadNums.main(java.lang.String[]) @bci=19, line=12 (Interpreted frame)

jstat

jstat 可以统计虚拟机状态参数信息.命令格式 jstat [ option vmid [ interval [ s | ms ] [ count ] ] ]

jmap

sudo jmap -heap 7604 可以查看JVM heap 参数.

僵尸进程(zombie)和孤儿进程(orphan)

孤儿进程: 一个父进程提前退出, 但是子进程还在运行, 子进程成为孤儿进程. 孤儿进程将被 init 进程(1号进程) 收养, 并由 init 进程对他们完成状态收集.

僵尸进程: 父进程使用 fork 创建子进程, 如果子进程先退出, 而父进程并没有调用 wait或 waitpid 获取子进程状态信息.

僵尸危害

由于进程退出之后并不是释放所有的资源,内核保留一定的信息,如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

查看僵尸进程个数

查看僵尸进程

ps -ef | grep defunct | grep -v grep

统计僵尸个数

ps -ef | grep defunct | grep -v grep | wc -l

如何杀死僵尸进程

僵尸进程很难用 kill 命令杀死.

1.利用信号通信机制

子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>

static void sig_child(int signo);

int main()
{
    pid_t pid;
    //创建捕捉子进程退出信号
    signal(SIGCHLD,sig_child);
    pid = fork();
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    else if (pid == 0)
    {
        printf("I am child process,pid id %d.I am exiting.\n",getpid());
        exit(0);
    }
    printf("I am father process.I will sleep two seconds\n");
    //等待子进程先退出
    sleep(2);
    //输出进程信息
    system("ps -o pid,ppid,state,tty,command");
    printf("father process is exiting.\n");
    return 0;
}

static void sig_child(int signo)
{
     pid_t        pid;
     int        stat;
     //处理僵尸进程
     while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
            printf("child %d terminated.\n", pid);
}

2. fork 两次

原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main()
{
    pid_t  pid;
    //创建第一个子进程
    pid = fork();
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    //第一个子进程
    else if (pid == 0)
    {
        //子进程再创建子进程
        printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid());
        pid = fork();
        if (pid < 0)
        {
            perror("fork error:");
            exit(1);
        }
        //第一个子进程退出
        else if (pid >0)
        {
            printf("first procee is exited.\n");
            exit(0);
        }
        //第二个子进程
        //睡眠3s保证第一个子进程退出,这样第二个子进程的父亲就是init进程里
        sleep(3);
        printf("I am the second child process.pid: %d\tppid:%d\n",getpid(),getppid());
        exit(0);
    }
    //父进程处理第一个子进程退出
    if (waitpid(pid, NULL, 0) != pid)
    {
        perror("waitepid error:");
        exit(1);
    }
    exit(0);
    return 0;
}