如何定位死循环或高cpu使用率

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何定位死循环或高cpu使用率相关的知识,希望对你有一定的参考价值。

定位死循环可以用i++的代码,高cpu使用率可以用i++进行循环电脑中所有的程序,进程树密集,占用电脑cpu就上去了,但是你这么做是什么目的呢,要攻击谁的电脑么。 参考技术A   估计压得越紧cpu和测温度的那电子原件贴的越紧,自然显示温度升的快,运行一段时间就算卡的很松温度也会由空气传过去。
  这个是物理现象另外那个测攻籂缔既郫焕惦唯定沥温的元件精度很差,基本上以10度为单位,显示60度实际温度可能在50-70
参考技术B 确定是CPU过高
使用top观察是否存在CPU使用率过高现象
找出线程
对CPU使用率过高的进程的所有线程进行排序
ps H -e -o pid,tid,pcpu,cmd --sort=pcpu |grep xxx
得到如下结果,其中线程2909使用了7.8%的CPU.
2907 2913 0.0 ./xxx
2907 2909 7.8 ./xxx
也可以通过查看/proc中的信息来确定高CPU线程. 打印了4列,线程ID,线程名,用户时间和内核时间(排名未分先后)
awk 'print $1,$2,$14,$15' /proc/2907/task/*/stat
找出调用栈
使用gdb attach nmsagent所在的进程,在gdb中使用 info threads显示所有线程
gdb
gdb>attach 2907
gdb>info threads
得到如下结果,可以发现2909线程的编号是12
13 Thread 0xad5f2b70 (LWP 2908) 0x004ef0d7 in mq_timedreceive () from /lib/tls/i686/cmov/librt.so.1
12 Thread 0xad58eb70 (LWP 2909) 0x006e0422 in __kernel_vsyscall ()
11 Thread 0xad52ab70 (LWP 2910) 0x006e0422 in __kernel_vsyscall ()
10 Thread 0xad4f8b70 (LWP 2911) 0x006e0422 in __kernel_vsyscall ()
9 Thread 0xad4c6b70 (LWP 2912) 0x006e0422 in __kernel_vsyscall ()
8 Thread 0xad3feb70 (LWP 2913) 0x004ef0d7 in mq_timedreceive () from /lib/tls/i686/cmov/librt.so.1
7 Thread 0xace08b70 (LWP 2914) 0x004ef0d7 in mq_timedreceive () from /lib/tls/i686/cmov/librt.so.1
6 Thread 0xac607b70 (LWP 2915) 0x006e0422 in __kernel_vsyscall ()
5 Thread 0xac5e6b70 (LWP 2916) 0x006e0422 in __kernel_vsyscall ()
4 Thread 0xac361b70 (LWP 2917) 0x006e0422 in __kernel_vsyscall ()
3 Thread 0xac2fdb70 (LWP 2918) 0x006e0422 in __kernel_vsyscall ()
2 Thread 0xac1fcb70 (LWP 2919) 0x004ef0d7 in mq_timedreceive () from /lib/tls/i686/cmov/librt.so.1
* 1 Thread 0xb78496d0 (LWP 2907) 0x006e0422 in __kernel_vsyscall ()
使用thread 切换线程,使用bt显示线程栈
gdb>thread 12
gdb>bt
得到如下线程栈
#0 0x006e0422 in __kernel_vsyscall ()
#1 0x001cca26 in nanosleep () from /lib/tls/i686/cmov/libc.so.6
#2 0x001fc2dc in usleep () from /lib/tls/i686/cmov/libc.so.6
#3 0x0806b510 in OspTaskDelay ()
#4 0x0805c710 in CDispatchTask::NodeMsgSendToSock() ()
#5 0x0805cc74 in DispatchTaskEntry ()
#6 0x0806a8e9 in OspTaskTemplateFunc(void*) ()
#7 0x00d4780e in start_thread () from /lib/tls/i686/cmov/libpthread.so.0
#8 0x002027ee in clone () from /lib/tls/i686/cmov/libc.so.6
ps + strace
得到进程ID 21465
ps -e |grep cmu
4996 ? 00:00:25 cmu_fjga_sp3
21465 pts/5 00:08:10 cmu
得到线程时间, 其中最占CPU的是 EpollRecvTask 21581
ps -eL |grep 21465
21465 21579 pts/5 00:00:00 CamApp
21465 21580 pts/5 00:00:00 TimerMan Task
21465 21581 pts/5 00:09:02 EpollRecvTask
21465 21582 pts/5 00:00:00
使用 strace -p 21581 得到线程栈本回答被提问者和网友采纳

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置

本文转载自微信公众号:Java识堂


1

top基本使用


top 命令运行图:


第一行:基本信息

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


第二行:任务信息

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


第三行:CPU使用情况

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


第四行:物理内存使用情况

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


buff/cache:


buffers 和 cache 都是内存中存放的数据,不同的是,buffers 存放的是准备写入磁盘的数据,而 cache 存放的是从磁盘中读取的数据 


在Linux系统中,有一个守护进程(daemon)会定期把buffers中的数据写入的磁盘,也可以使用 sync 命令手动把buffers中的数据写入磁盘。使用buffers可以把分散的 I/O 操作集中起来,减少了磁盘寻道的时间和磁盘碎片。 
cache是Linux把读取频率高的数据,放到内存中,减少I/O。Linux中cache没有固定大小,根据使用情况自动增加或删除。


第五行:交换区使用情况

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


Swap(内存交换区):


  是硬盘上的一块空间。在内存不足的情况下,操作系统把内存中不用的数据存到硬盘的交换区,腾出内存来让别的程序运行。因此,开启swap会一定程度的引起 I/O 性能下降(阿里服务器默认不开)


第六行:进程详细信息

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


2

死循环


构造的代码如下:

@RestController
@RequestMapping("top")
public class ShowTopController {

    private Object lock1 = new Object();
    private Object lock2 = new Object();

    @RequestMapping("test")
    public String test() {
        return "success";
    }

    @RequestMapping("loop")
    public String loop() {
        System.out.println("start");
        while (true) {}
    }

    @RequestMapping("deadlock")
    public String deadlock() {
        new Thread(() -> {
            synchronized (lock1) {
                try{
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {}
                synchronized (lock2) {
                    System.out.println("thread1 over");
                }
            }
        }).start();
        new Thread(() -> {
            synchronized (lock2) {
                try{
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {}
                synchronized (lock1) {
                    System.out.println("thread2 over");
                }
            }
        }).start();
        return "success";
    }
}

这里只介绍一下用到的top参数

参数选项名称

含义

p

通过指定进程ID(PID)来仅仅监控某个进程的状态。可以指定多个,-pN1 -pN2 … (-p N1 -p N2…也可)或者 -pN1,N2,N3 …(-p N1,N2…也可)

H

显示所有线程的运行状态指标。如果没有该参数,会显示一个进程中所有线程的总和。在运行过程中,可以通过H命令进行交互控制


先手动制造CPU飙高的场景,多执行几次,小编这里执行3次


curl localhost:8080/top/loop


执行top

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


jstack命令工具可以得到线程堆栈信息,根据这些线程堆栈信息,我们可以去检查Java程序出现的问题


看到pid为23757的进程CPU占用较高,执行如下命令

jstack 23757 > loop.txt


看看pid为23757的进程中线程的具体情况

top -p 23757 -H


线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


当然你也可以使用交互命令

top -p 23757


然后再输入H,效果和上面一样


可以看到PID为23772,23773和23774的线程占用CPU较高


这里可能有人有疑惑,为什么线程也有PID啊?其实线程进程都会有自己的ID,这个ID就叫做PID,PID是不特指进程ID,线程ID也可以叫做PID


将10进制的23772转为16进制,因为jstack中PID用的是16进制


printf "%x" 23772
输出5cdc


打开loop.txt文件,搜5cdc

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


可以看到线程一直在执行ShowTopController中的第23行,即


while (true) {}


好了定位到代码中的位置了,当然生产环境中肯定不会写一个死循环的,有可能在特殊场景下出现死循环,或执行一个方法特别慢,用这种方法很快就能找到代码位置。


3

死锁

接着访问
curl localhost:8080/top/loop

执行
jstack 23757 > loop.txt

打开loop.txt文件到最后
线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置


看到发现一个死锁,死锁代码的位置描述的很清楚,生产环境发生的死锁当然没有这么简单,所有学会用这些命令排查还是很有必要的



在GitHub更新中,欢迎关注,欢迎star。


 

直面Java第206期:什么是正则表达式

成神之路第015期:深入学习Java中的IO

深入并发第006期:Java线程池的正确使用姿势

- MORE | 更多精彩文章 -





如果你喜欢本文。

请长按二维码,关注Hollis

以上是关于如何定位死循环或高cpu使用率的主要内容,如果未能解决你的问题,请参考以下文章

[原]调试实战——程序CPU占用率飙升,你知道如何快速定位吗?

线上CPU飚高(死循环,死锁...)?帮你迅速定位代码位置

三步排查JVM cpu 100%问题

代码死循环导致cpu使用率过高

Linux环境下使用Arthas排查cpu飙高问题

python GIL锁与多cpu