堆栈分析 _ 如何寻找占用 CPU 资源最多的代码

使用场景:服务器卡顿,cpu99%, 内存正常, 数据库正常

ps: 大部分内容摘自 kim 培训视频
—————————————- 第一部分基础概念—————————————

1.java 线程类型

daemon threads:守护线程, 即使你不创建任何线程,JAVA 应用也将默认创建几个线程。他们大部分是 daemon threads。主要用于任务处理比如内存回收或者是 JMX

非 daemon threads

2.java 线程状态

  • NEW:线程刚被创建,但是还没有被处理。
  • RUNNABLE:线程占用了 CPU 并且处理了一个任务。(或是是在等待状态由于操作系统的资源分配)
  • BLOCKED:该线程正在等待另外的不同的线程释放锁,以便获取监视器锁
  • WAITING:该线程正在等待,通过使用了 wait, join 或者是 park 方法
  • TIMED_WAITING:该线程正在等待,通过使用了 sleep, wait, join 或者是 park 方法。(这个与 WAITING 不同是通过方法参数指定了最大等待时间,WAITING 可以通过时间或者是外部的变化解除)
    image.png

我们来一段一段分析

1.Atmosphere-Scheduler-16934 线程名称

2.daemon 线程类型, 这里是守护线程

3.prio=6 线程优先级, 貌似在代码里可以指定优先级(在 java 中设置线程优先级使用 setPriority)

4.tid=0x000000002ef09800 java 进程 id,可通过 java.lang.Thread.getId() 获得,常用自增长的长整型

5.nid=0xa50 原生线程 id,此 id 可以让你获得诸如从操作系统的角度来看哪个线程在你的 jvm 中占用了大部分的 cpu 时间等信息。使用工具或者命令行获取的线程 TID 一般是十进制,转换成十六进制之后就是这里的 nid!!!!!! 这个属性非常重要

6. 最后的一段 java 线程状态和详细信息,可以通过这段信息获取到当前线程极有可能阻塞的原因

3.Jstack

Jstack 是 Jdk 自带的线程跟踪工具,用于打印指定 Java 进程的线程堆栈信息。

pid 填写任务管理器的 pid

jstack -l [pid]

image.png

—————————————- 第二部分: 分析思路—————————————

线程堆栈的本质其实是 java 线程实际上在当时时间点上的运行情况,由此可以衍生出几种分析思路

1. 直接通过工具获取当前消耗资源最多的线程 TID(注意是 TID)转换成十六进制之后可以在堆栈里直接找到对应的线程问题

2. 由于线程名称可以自定义,所以可以尝试在线程名称上加上进程开始时间戳 ( 网站搜索 911 查询 里面有时间戳批量转换工具 十分方便 )以及线程是入口还是结束,以此来进行分析可以迅速分析出哪些线程运行时间比较长,找到问题点 ( 这一段在 20170905 公司培训比较里可以看到具体的例子)

3. 如果上述两个方法都找不到问题,那么就要手动去观察堆栈里哪些线程可能存在问题,比如 多次对比哪些线程在多份堆栈里多次存在(多次存在就代表执行时间比较长),分析出事项目本身代码的问题还是第三方工具导致的问题

4. 实际上 80% 的问题都可以通过线程堆栈来解决,如果涉及到内存方面,这个问题就会比较复杂,需要其他知识去解决

—————————————- 第三部分实际操作—————————————如果是 windows 平台下 推荐使用 Process Explorer 来获取占用 cpu 最高的线程

image.png

1. 在资源管理器找到 java 进程 id 使用 jstack 抓取堆栈

最好隔几秒抓一次

2. 使用 Process Explorer 查找占用资源最多的进程 TID

直接找到 java 进程右键点击 然后选择 Properties 然后在里面点击 Threads 按照 cpu 来排序,就可以找到 cpu 占用最高的 TID

比如我们用 Process explorer 中找到 java 中 cpu 占用最高的 TID 是 7608 我们将它转换为四字十六进制然后去堆栈信息里面找 就会找到对应的堆栈
image.png

image.png

找到具体的堆栈之后 先试试能不能找到具体的代码信息 如果能找到代码信息就可以开始分析了

processexplorer.zip

UltraEdit.rar 此文件请前往有道云笔记下载