当前位置: 主页 > JAVA语言

java如何排查内存泄露-java内存泄漏怎么排查问题?:常规查询方法

发布时间:2023-06-22 09:15   浏览次数:次   作者:佚名

一、常规查询方法

当我们遇到JAVA内存泄漏或者CUP居高不下的时候,一般怎么排查问题呢?

首先我们看段代码,以下代码是当用户输入任意字符之后,开始启动三个线程,一个死循环,一个锁竞争,一个死锁。启动之后我们来看下CUP的一个变化。

1、CPU 100%代码片段

package com.netty;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
 * 作者:XX
 * 创建日期:2021/8/16
 * 类说明:模拟CPU 占用 100%
 *
 */
public class TestCpuThread {
    public static void main(String[] args) throws IOException {
        //控制台输入控制
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        br.readLine();
        //死循环线程
        createBusyThread();
        br.readLine();
        Object o = new Object();
        createLockThread(o);
        //死锁
        createDeadLock();
    }
    public static void createBusyThread() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true)
                    ;
            }
        }, "busyThreadName");
        t.start();
    }
    public static void createLockThread(final Object lock) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(lock) {
                    try {
                        lock.wait();
                    }catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"lockThreadName");
        t.start();
    }
    public static void createDeadLock() {
        Object a = new Object();
        Object b = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (a) {
                    try {
                        Thread.sleep(3000);
                        synchronized (b) {
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (b) {
                    try {
                        Thread.sleep(3000);
                        synchronized (a) {
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"t2");
        t1.start();
        t2.start();
    }
}

启动前

没有启动任何JAVA线程java如何排查内存泄露,CUP占用为0%

centos内存槽道排查_python 内存泄漏排查_java如何排查内存泄露

centos内存槽道排查_java如何排查内存泄露_python 内存泄漏排查

启动后

centos内存槽道排查_python 内存泄漏排查_java如何排查内存泄露

python 内存泄漏排查_centos内存槽道排查_java如何排查内存泄露

centos内存槽道排查_python 内存泄漏排查_java如何排查内存泄露

centos内存槽道排查_java如何排查内存泄露_python 内存泄漏排查

我们发现CPU被打满100%。进程为我们启动的程序。这个时候我们肯定想知道线程都在干什么。导致CUP消耗过高!那么具体怎么排查呢?

2、问题排查获取进程ID

centos内存槽道排查_java如何排查内存泄露_python 内存泄漏排查

通过top命令可以看到,最消耗CUP的进程ID。如上图,得到进程ID为3030

查看进程内的线程ID

得到进程ID之后,我们可以通过

top -Hp 3030

命令查看进程内的线程ID,如下,找到最好CUP的线程ID3051。

centos内存槽道排查_java如何排查内存泄露_python 内存泄漏排查

将线程ID转为16进制

centos内存槽道排查_python 内存泄漏排查_java如何排查内存泄露

printf "%x\n" 3051

centos内存槽道排查_python 内存泄漏排查_java如何排查内存泄露

jstack命令查看线程执行情况

通过java自带的jstack命令导出栈信息。发现是busyThreadName线程在执行。查看代码发现死循环java如何排查内存泄露,导致CUP100%。

jstack 3030 | grep beb

java如何排查内存泄露_centos内存槽道排查_python 内存泄漏排查

java如何排查内存泄露_centos内存槽道排查_python 内存泄漏排查

二、show-busy-threads 脚本

但是每次查找都要执行那么多命令实在有点麻烦,不如我们就整合一下,把查找过程放在一个脚本里,岂不是美哉。

centos内存槽道排查_python 内存泄漏排查_java如何排查内存泄露

代码如下

#!/bin/bash
# @Function
# Find out the highest cpu consumed threads of java, and print the stack of these threads.
# $ ./show-busy-threads
#ARGS= -p  pid
#[ $? -ne 0] 
PROG=`basename $0`
count=3
redEcho() {
[ -c /dev/stdout ] && {
echo -ne "\003[1;31m"
echo -n "$@"
echo -e "\0ee[0m"
} || echo "$@"
}
	if ! which jstack &> /dev/null; then
    [ -n "$JAVA_HOME" ] && [ -f "$JAVA_HOME/bin/jstack" ] && [ -x "$JAVA_HOME/bin/jstack" ] && {
        export PATH="$JAVA_HOME/bin:$PATH"
    } || {
        redEcho "Error: jstack not found on PATH and JAVA_HOME!"
        exit 1
    }
fi
uuid=`date +%s`_${RANDOM}_$
cleanupWhenExit() {
    rm /tmp/${uuid}_* &> /dev/null
}
trap "cleanupWhenExit" EXIT
printStackOfThread() {
    while read threadLine ; do
        pid=`echo ${threadLine} | awk '{print $1}'`
        threadId=`echo ${threadLine} | awk '{print $2}'`
        threadId0x=`printf %x ${threadId}`
        user=`echo ${threadLine} | awk '{print $3}'`
        pcpu=`echo ${threadLine} | awk '{print $5}'`
        jstackFile=/tmp/${uuid}_${pid}
        [ ! -f "${jstackFile}" ] && {
            jstack ${pid} > ${jstackFile} || {
                redEcho "Fail to jstack java process ${pid}!"
                rm ${jstackFile}
                continue
            }
        }
        redEcho "The stack of busy(${pcpu}%) thread(${threadId}/0x${threadId0x}) of java process(${pid}) of user(${user}):"
        sed "/nid=0x${threadId0x}/,/^$/p" -n ${jstackFile}
    done
}
[ -z "${pid}" ] && {
    ps -Leo pid,lwp,user,comm,pcpu --no-headers | awk '$4=="java"{print $0}' |
    sort -k5 -r -n | head --lines "${count}" | printStackOfThread
} || {
    ps -Leo pid,lwp,user,comm,pcpu --no-headers | awk -v "pid=${pid}" '$1==pid,$4=="java"{print $0}' |
    sort -k5 -r -n | head --lines "${count}" | printStackOfThread
}

此命令通过结合Linux操作系统的ps命令和jvm自带的jstack命令,查找Java进程内CPU利用率最高的线程,一般适用于服务器负载较高的场景,并需要快速定位导致负载高的原因。

本脚本来自一个叫候鸟树的网友,原作者不详,这里保留原作者名为了表示对技术人的尊重,在他的脚本的基础上做了一些改动。

python 内存泄漏排查_java如何排查内存泄露_centos内存槽道排查

命令格式:

./show-busy-threads -p 进程号

使用示例:

./show-busy-threads -p 3030

示例输出:

java如何排查内存泄露_centos内存槽道排查_python 内存泄漏排查

好啦,以后查找比较耗CPU的线程就比较好找了。另外大家在使用线程工作的时候尽量自己命名线程名称,方便后期问题排查。

原文