java 一次CPU占用过高问题的排查及解决

最近一段时间  某台服务器上的一个应用总是隔一段时间就自己挂掉      用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. 通知消息队列
 */

从很有年代感的垃圾代码来看  这两个方法并没有什么问题  继续往调用这两个方法的上层排查

/**
 * 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)  无数个草泥马策马奔腾           ps:垃圾代码看多了, 我已经不愤怒了. 

 基本定位到问题了      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占用就很正常了

案例一下,其实之前也遇到过CPU占用很高的问题,  但是那次是  频繁的GC导致的

其实排查问题 的过程中也是在不断的学习的过程 ! 先打个鸡血,我要继续搬砖了

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Kevin-ZhangCG

[ Java面试题 ]持久层篇

2956
来自专栏Seebug漏洞平台

Spring MVC 目录穿越漏洞(CVE-2018-1271)分析

2018年04月05日,Pivotal公布了Spring MVC存在一个目录穿越漏洞(CVE-2018-1271)。Spring Framework版本5.0到...

4462
来自专栏腾讯数据库技术

比ls快8倍?百万级文件遍历的奇技淫巧

1.3K4
来自专栏黑泽君的专栏

day53_BOS项目_05

定区可以将取派员、分区、客户信息关联到一起。 页面:WEB-INF/pages/base/decidedzone.jsp

914
来自专栏Linux驱动

第3阶段——内核启动分析之start_kernel初始化函数(5)

内核启动分析之start_kernel初始化函数(init/main.c) stext函数启动内核后,就开始进入start_kernel初始化各个函数, 下面只...

31410
来自专栏Java帮帮-微信公众号-技术文章全总结

JavaWeb13-设计模式案例实现(Java真正的全栈开发)

? JavaWeb设计模式&案例 一.JavaWeb的设计模式 1. jsp模式介绍 SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一...

3336
来自专栏王清培的专栏

.NET应用程序调试—原理、工具、方法

阅读目录: 1.背景介绍 2.基本原理(Windows调试工具箱、.NET调试扩展SOS.DLL、SOSEX.DLL) 2.1.Windows调试工具箱 ...

2406
来自专栏安恒网络空间安全讲武堂

堆利用之double-free

3264
来自专栏码洞

Java高阶必备之Netty基础原理

Netty是Java程序员通向高阶之路必须要过的门槛之一。干了几年的Java程序员发现业务开发似乎就是在SSH的世界里摸滚打爬的时候,会开始感到迷茫,难道程序员...

1032
来自专栏java达人

使用Redis做MyBatis的二级缓存

使用Redis做MyBatis的二级缓存  通常为了减轻数据库的压力,我们会引入缓存。在Dao查询数据库之前,先去缓存中找是否有要找的数据,如果有则用缓存中的数...

4535

扫码关注云+社区

领取腾讯云代金券