linux jstack 分析,使用top和jstack分析高CPU问题-程序员宅基地

技术标签: linux jstack 分析  

通常我们所说的 CPU 使用率过高,这里面其实隐含着一个用来比较高与低的基准值,比如 JVM 在峰值负载下的平均 CPU 利用率为 40%,如果 CPU 使用率飙到 80% 就可以被认为是不正常的。

典型的 JVM 进程包含多个 Java 线程,其中一些在等待工作,另一些则正在执行任务。在单个 Java 程序的情况下,线程数可以非常低,而对于处理大量并发事务的互联网后台来说,线程数可能会比较高。

对于 CPU 的问题,最重要的是要找到是哪些线程在消耗 CPU,通过线程栈定位到问题代码;如果没有找到个别线程的 CPU 使用率特别高,我们要怀疑到是不是线程上下文切换导致了 CPU 使用率过高。下面我们通过一个实例来学习 CPU 问题定位的过程。

定位高 CPU 使用率的线程和代码

写一个模拟程序来模拟 CPU 使用率过高的问题,这个程序会在线程池中创建 4096 个线程。代码如下:

@SpringBootApplication

@EnableScheduling

public class DemoApplication {

//创建线程池,其中有4096个线程。

private ExecutorService executor = Executors.newFixedThreadPool(4096);

//全局变量,访问它需要加锁。

private int count;

//以固定的速率向线程池中加入任务

@Scheduled(fixedRate = 10)

public void lockContention() {

IntStream.range(0, 1000000)

.forEach(i -> executor.submit(this::incrementSync));

}

//具体任务,就是将count数加一

private synchronized void incrementSync() {

count = (count + 1) % 10000000;

}

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

}

在 Linux 环境下启动程序:

java -Xss256k -jar demo-0.0.1-SNAPSHOT.jar

请注意,这里我将线程栈大小指定为 256KB。对于测试程序来说,操作系统默认值 8192KB 过大,因为我们需要创建 4096 个线程。

使用 top 命令,我们看到 Java 进程的 CPU 使用率达到了 262.3%,注意到进程 ID 是 4361。

c282ecd80989

image.png

接着我们用更精细化的 top 命令查看这个 Java 进程中各线程使用 CPU 的情况:

#top -H -p 4361

c282ecd80989

image.png

从图上我们可以看到,有个叫“scheduling-1”的线程占用了较多的 CPU,达到了 42.5%。因此下一步我们要找出这个线程在做什么事情。

为了找出线程在做什么事情,我们需要用 jstack 命令生成线程快照,具体方法是:

jstack 4361

jstack 的输出比较大,你可以将输出写入文件:

jstack 4361 > 4361.log

然后我们打开 4361.log,定位到第 4 步中找到的名为“scheduling-1”的线程,发现它的线程栈如下:

c282ecd80989

image.png

从线程栈中我们看到了AbstractExecutorService.submit这个函数调用,说明它是 Spring Boot 启动的周期性任务线程,向线程池中提交任务,这个线程消耗了大量 CPU。

进一步分析上下文切换开销

一般来说,通过上面的过程,我们就能定位到大量消耗 CPU 的线程以及有问题的代码,比如死循环。但是对于这个实例的问题,你是否发现这样一个情况:Java 进程占用的 CPU 是 262.3%, 而“scheduling-1”线程只占用了 42.5% 的 CPU,那还有将近 220% 的 CPU 被谁占用了呢?

不知道你注意到没有,我们在第 4 步用top -H -p 4361命令看到的线程列表中还有许多名为“pool-1-thread-x”的线程,它们单个的 CPU 使用率不高,但是似乎数量比较多。你可能已经猜到,这些就是线程池中干活的线程。那剩下的 220% 的 CPU 是不是被这些线程消耗了呢?

要弄清楚这个问题,我们还需要看 jstack 的输出结果,主要是看这些线程池中的线程是不是真的在干活,还是在“休息”呢?

c282ecd80989

image.png

通过上面的图我们发现这些“pool-1-thread-x”线程基本都处于 WAITING 的状态,那什么是 WAITING 状态呢?或者说 Java 线程都有哪些状态呢?你可以通过下面的图来理解一下:

c282ecd80989

image.png

从图上我们看到“Blocking”和“Waiting”是两个不同的状态,我们要注意它们的区别:

Blocking 指的是一个线程因为等待临界区的锁(Lock 或者 synchronized 关键字)而被阻塞的状态,请你注意的是处于这个状态的线程还没有拿到锁。

Waiting 指的是一个线程拿到了锁,但是需要等待其他线程执行某些操作。比如调用了 Object.wait、Thread.join 或者 LockSupport.park 方法时,进入 Waiting 状态。前提是这个线程已经拿到锁了,并且在进入 Waiting 状态前,操作系统层面会自动释放锁,当等待条件满足,外部调用了 Object.notify 或者 LockSupport.unpark 方法,线程会重新竞争锁,成功获得锁后才能进入到 Runnable 状态继续执行。

回到我们的“pool-1-thread-x”线程,这些线程都处在“Waiting”状态,从线程栈我们看到,这些线程“等待”在 getTask 方法调用上,线程尝试从线程池的队列中取任务,但是队列为空,所以通过 LockSupport.park 调用进到了“Waiting”状态。那“pool-1-thread-x”线程有多少个呢?通过下面这个命令来统计一下,结果是 4096,正好跟线程池中的线程数相等。

c282ecd80989

image.png

你可能好奇了,那剩下的 220% 的 CPU 到底被谁消耗了呢?分析到这里,我们应该怀疑 CPU 的上下文切换开销了,因为我们看到 Java 进程中的线程数比较多。下面我们通过 vmstat 命令来查看一下操作系统层面的线程上下文切换活动:

c282ecd80989

image.png

其中 cs 那一栏表示线程上下文切换次数,in 表示 CPU 中断次数,我们发现这两个数字非常高,基本证实了我们的猜测,线程上下文切切换消耗了大量 CPU。那么问题来了,具体是哪个进程导致的呢?

我们停止 Spring Boot 测试程序,再次运行 vmstat 命令,会看到 in 和 cs 都大幅下降了,这样就证实了引起线程上下文切换开销的 Java 进程正是 4361。

c282ecd80989

image.png

总结

当我们遇到 CPU 过高的问题时,首先要定位是哪个进程的导致的,之后可以通过top -H -p pid命令定位到具体的线程。其次还要通 jstack 查看线程的状态,看看线程的个数或者线程的状态,如果线程数过多,可以怀疑是线程上下文切换的开销,我们可以通过 vmstat 和 pidstat 这两个工具进行确认。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_29595351/article/details/116906939

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签