性能分析之CPU高到定位到代码行(JAVA)

Posted zuozewei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了性能分析之CPU高到定位到代码行(JAVA)相关的知识,希望对你有一定的参考价值。

前言

通常情况下,性能报告中只说 CPU 使用率高的时候,并不能帮助定位问题。因为 CPU 高会有多种不同的情况。CPU 有五种状态(us sy id wa st), 在 vmstat 中能显示出来,这个想必很多人都清楚。在代码消耗CPU的时候(这也是通常性能分析中会遇到的),是 US 状态的CPU。当然还存在一种情况,就是代码产生的系统调用特别高,这种情况下SY的 CPU 也会高(这种情况比较少见,在我的职业生涯中只见过一次)。对于 Java 语言来说,我们不需要特别复杂的 profile工具就可以做到定位到代码。

线程状态转换关系

在写具体的分析方法之前,需要说一下线程的状态转换关系,我们先来看一下系统级的线程状态转换关系。


通过这个转换关系,可以看到,在线程产生之后,会先到 ready 的状态。在这个状态上是在等待 CPU 的。而在 runing 状态才是真正在 CPU 上执行的。请注意这个区别。
而 vmstat 显示的 r 列是包括了 ready 和 running 的线程(会因操作系统的不同有区别,但是对大部分 Linux 系统都是这样)。
请注意这一点,因为网上有很多在写 vmstat 的解释的时候,说 R 列是表示正在运行的进程数或者说 R 是表示正在运行的线程数,这一点是不对的。

给大家看一个例子(这也是下面要说到的例子):

这是我的一个云服务器上正在运行的 top。可以看到当前 tasks(进程)中只有一个是 running 的状态的。而这时的 vmstat 呢?

这个服务器只有两个 CPU,所以如果 R 是说正在运行的进程或线程数的话肯定是不正确的,因为两个 CPU 同时运行的最多是两个线程。

所以请记住,这个 R 值就包括了等待 CPU 的线程(也就是 ready 状态的)和正在运行的线程(也就是 running 状态的)。

以后有时间再解释其他系统级的线程状态,可能有些人觉得其他状态也没什么好解释的,但是在性能分析中,线程的状态和一些性能计数器是有关联关系的,比如说suspended 状态是 CPU 时间片用完导致暂时被换出;而 blocked 是因为要等待某个条件被满足而阻塞;并且这两种状态都有可能导致 CPU 使用率高。在分析的过程中,这些信息给我们的是一个方向。所以前面说到仅说 CPU 高不能帮助分析问题,因为 CPU 高有多种原因。

因为下面要说的是 Java,所以来看一下 Java 的线程状态转换关系。

从这个图中,我们可以看到 Java 的进程有多种状态(具体状态的解释请自行搜索),怎么看到这些状态都在干什么,就需要把栈打出来看。在栈中,可以看到具体对应的代码(对其他编译语言来说,要看看到正在运行的 code 也是要看栈的)。

另外,在性能分析中,栈的分析是非常重要的一块内容,今天因为只是为了说明从 CPU 高怎么定位到代码层,所以不过多解释线程的状态了,以后有时间再写文章说明。

实例分析

下面来操作一下,首先执行一个消耗 CPU 的 Java 实例。查看 vmstat 的状态。

从上图可以看到左边窗口在执行一个消耗 CPU 的 Demo,而右边的窗口看到当前系统的 CPU 已经完全被消耗掉了。进程号通过 top 命令就可以知道:

下面就要看一下这个进程中的哪些线程消耗了C PU。
通过 pidstat 可以看到(不好意思的是,pidstat 的截图被我覆盖掉了,又不想重新开始所以就不截图啦),有 10 个线程消耗着 CPU 资源。我把命令放在这里,有兴趣的可以自己操作。

pidstat -p 10846 -u -d -t -w -h 1 1000  

从上命令可以看到,有多个线程消耗着 CPU。线程 ID 是:10861、10862、10863 等等。

用 jstack 做一下 thread dump。

[root@7dgroup ~]# jstack -l 10846 > 10846.threaddump

再打开这个生成的文件。

nid 是指 native ID,对应着系统级的 tid。只不过 TID 显示的是 10 进制的,NID 显示的是 16 进制的。

我们转换一个线程号来查找。

[root@7dgroup ~]# printf %x'\\n' 10861
2a6d

再对应到 threaddump 文件中。

显然可以去查这个 CPUTestThreadDemo.java 的第 13 行了。

从这个例子可以看出,对 Java 的代码消耗 CPU 高的分析只需要通过系统级的命令和 JDK 自带的命令就可以完成了。
因为这个例子非常简单,步骤比较清楚。但在实际分析代码众多,逻辑复杂的应用,有可能你看到的是 CPU 在线程上的消耗是在不停的切换的,所以就需要多做些 thread dump,一个个分析。当然借助些工具分析,通常可以让我们在分析复杂的应用时事半功倍。

这里只说明一个思路。

小结

对 Java 的分析来说,已经有非常多的人写了非常多的文章了,我之所以写这个文章,是为了让文章能更系列一些。

以上是关于性能分析之CPU高到定位到代码行(JAVA)的主要内容,如果未能解决你的问题,请参考以下文章

性能分析之从 CPU 高到定位到代码行(C/C++)

性能分析之从 CPU 高到定位到代码行(C/C++)

JVM调优之jstack找出最耗cpu的线程并定位代码

性能分析之IO分析-从IO高到具体文件

Java代码分析和CPU高的方法分析

性能测试指标之CPU时间片