好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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 方法有问题

一下为精简的部分代码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

/** * 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 ;

   }

  }

}

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

/**

  * 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里面完全是没有用的代码

继续往上层看谁来调用

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

/**

  * 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

查看更多关于java实战CPU占用过高问题的排查及解决的详细内容...

  阅读:59次