java实战CPU占用过高问题的排查及解决
作者:江湖前辈黄药师 发布时间:2023-01-14 21:46:49
最近一段时间 某台服务器上的一个应用总是隔一段时间就自己挂掉 用top看了看 从重新部署应用开始没有多长时间CPU占用上升得很快
排查步骤
1.使用top 定位到占用CPU高的进程PID
top
2.通过ps aux | grep PID命令
获取线程信息,并找到占用CPU高的线程
ps -mp pid -o THREAD,tid,time | sort -rn
3.将需要的线程ID转换为16进制格式
printf "%x\n" tid
4.打印线程的堆栈信息 到了这一步具体看堆栈的日志来定位问题了
jstack pid |grep tid -A 30
top 可以看出PID 733进程 的占用CPU 172%
查找进程733下的线程 可以看到TID 线程775占用了96%且持有了很长时间 其实到这一步基本上能猜测到应该是 肯定是那段代码发生了死循环
ps -mp 733 -o THREAD,tid,time | sort -rn
线程ID转换为16进制格式
printf "%x\n" 775
查看java 的堆栈信息
jstack 733 |grep 307 -A 30
显然是 SmsQueueServiceImpl 中的produceMissSms 和 consumeMissSms 方法有问题
一下为精简的部分代码
/** * Created by dongxc on 2015/7/7. 通知消息队列 */
@Service("smsQueueService")
public class SmsQueueServiceImpl {
// 生产异常队列方法
public void produceMissSms(SmsLogDo smsLogDo) {
/*
* try{ String key = EnumRedisPrefix.SMS_QUEUE_MISS_DEAL.getValue(); boolean result = redisService.lpush(key,
* smsLogDo, 0); if(result==false){ logger.error("通知消息异常队列生产消息返回失败!"+smsLogDo.getId()); } }catch(Exception e){
* logger.error("通知消息异常队列生产消息失败!", e); }
*/
}
// 消费异常队列方法
public SmsLogDo consumeMissSms() {
try {
String destKey = EnumRedisPrefix.SMS_QUEUE_MISS_DEAL.getValue();
SmsLogDo smsLogDo = new SmsLogDo();
Object obj = null;
if (obj == null) {
return null;
} else {
smsLogDo = (SmsLogDo) obj;
}
return smsLogDo;
} catch (Exception e) {
logger.error("通知消息队列消费方法失败!", e);
return null;
}
}
}
从很有年代感的垃圾代码来看 这两个方法并没有什么问题 继续往调用这两个方法的上层排查
/**
* Created by dongxc on 2015/7/7.
* 消息通知监控线程
*/
@Service("smsMonitorComsumer")
public class SmsMonitorComsumerImpl {
@Autowired
private SmsQueueServiceImpl smsQueueService;
//取队列里的任务消费
@Transactional(propagation= Propagation.NOT_SUPPORTED)
public void run() {
while (true) {
try {
SmsLogDo smsLogDo = smsQueueService.consumeMissSms();
Boolean result = false;
if(smsLogDo!=null){
long diff = (new Date()).getTime() - smsLogDo.getSendtime().getTime() ;
long min = diff%(1000*24*60*60)%(1000*60*60)/(1000*60);//计算差多少分钟
if(min>5){
result = true;
}
}
if(result){
smsQueueService.produceSms(smsLogDo);
}else{
smsQueueService.produceMissSms(smsLogDo);
}
} catch (Exception ex) {
try{
Thread.sleep(3000);
}catch(Exception e){
//logger.error("发送站内信息短信时线程执行失败2!", e);
}
}
}
}
}
很显然 这里有一个while(true) 基本定位到问题了 while里面完全是没有用的代码
继续往上层看谁来调用
/**
* Created by dongxc on 2015/7/7.
* 通知消息队列
*/
@Service("smsLogRunThread")
public class SmsLogRunThreadImpl {
public int flag;
@Autowired
private SmsLogConsumerImpl smsLogConsumer;
@Autowired
private SmsMonitorComsumerImpl smsMonitorComsumer;
@PostConstruct
public void init() {
if(ip!=""&&host!=""&&ip.equals(host)){
Thread thread = new Thread(){
public void run() {
smsLogConsumer.run();
}
};
thread.start();
Thread thread1 = new Thread(){
public void run() {
smsMonitorComsumer.run();
}
};
thread1.start();
}
}
}
在应用一启动的时候 spring初始化的就会执行这一段处理丢失消息的代码 然后这段死循环代码 没有任何作用
解决方法 即 注释掉whlie(true)这一段代码
案例一下,其实之前也遇到过CPU占用很高的问题, 但是那次是 频繁的GC导致的
其实排查问题 的过程中也是在不断的学习的过程
来源:https://www.cnblogs.com/xxj0316/p/9448987.html


猜你喜欢
- 条码的应用已深入生活和工作的方方面面。在处理条码时,常需要和各种文档格式相结合。当需要在文档中插入、编辑或者删除条码时,可借助于一些专业的类
- AlertDialog可以在当前的界面上显示一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力,因此AlertD
- 根据狂神的视频做的,然后自己优化了一些bug,比如新生成食物的时候不会生成在蛇的身体上,再新增长身体的时候不会在左上角出现一个绿色的方块以及
- 不记得从哪找的了,修改了部分代码,修复在Android平台下使用时,时区时间格式异常的问题。package cn.aikongmeng.de
- <?xml version="1.0" encoding="UTF-8" ?> <
- 目录一、System.out.println(最简单)二、java.util.logging(相对简单)三、log4j(最强大)四、comm
- 简介记录一个利用系统分享功能进行图片分享的工具类(代码是用Kotlin写的,都是比较简单的语法,部分可能需要自定义的地方都已经标出)。调用方
- 一、JPA介绍JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系
- Web基础和HTTP协议┌─────────┐┌─────────┐
- ⛳️ 基本类型做形式参数(零散参数的数据接收)1、基本数据类型要求前台页面的表单输入框的name属性值与对应控制器方法中的形式参数名称与类型
- 在Java中,HashMap是一种常用的数据结构,它以键值对的形式存储和管理数据。然而,由于HashMap在多线程环境下存在线程安全问题,因
- 1. 下载Tomcat首先,下载Apache Tomcat并解压到本地计算机,可存放于任何位置。另外,需要在系统中环境JRE_H
- 这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。例如:&
- 本文为大家介绍了java.util.ArrayDeque类使用方法,供大家参考,具体内容如下1. ArrayDeque有两个类属性,head
- 从何说起前些天和朋友讨论一个问题,他们的应用有几十万会员然后对应有积分,现在想做积分排名的需求,问有没有什么好方案。这个问题也算常见,很多地
- C#写入xml文件1、XmlDocument1。我认为是最原始,最基本的一种:利用XmlDocument向一个XML文件里写节点,然后再利用
- 起因曾经用过西门子出的 * , 好处是直接有SDK开发包, 不会硬件开发也能直接使用缺点也是明显的, 就是只支持Windows系统, 另外就
- Android 自动获取验证码的两种方式分别是BroadcastReceiver及ContentObserver,两种方式都需要进行注册、取
- 一、问题描述在接受 mq 消息的时候,需要做一个重试次数限制,如果超过 maxNum 就发邮件告警,不再重试。 所以我需
- 1,首先要安装OpenCvSharp,使用cv2的函数获取设备,并将图像转换到Bitmapusing OpenCvSharp;