• 谈谈Tomcat占用cpu高的问题


    目录

    问题现场

    测试环境tomcat进程占用CPU一直持续99%,但是通过jstack查看log,也没有任何线程死锁的情况。
    此时通过$catalina_home/bin/shutdown.sh脚本无法正常停止tomcat。

    这是什么原因?

    线程死锁 vs 线程死循环

    验证线程死锁不会导致CPU持续高负载

    // 验证线程死锁是否会导致CPU占用率一直居高不下
    public class LockTest {
        private Object alock = new Object();  // a锁
        private Object block = new Object();  // b锁
    
        public static void main(String[] args) {
            new LockTest().start();
        }
    
        private void start() {
            new Thread(new ThreadA()).start();
            new Thread(new ThreadB()).start();
        }
    
        private void sleepOfSeconds(int seconds) {
            try {
                Thread.sleep(seconds * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        // 在线程A中先获取a锁,再获取b锁
        class ThreadA implements Runnable {
            private String name = "ThreadA";
            @Override
            public void run() {
                synchronized (alock) {
                    System.out.println(name + " get alock");
                    sleepOfSeconds(50);
                }
                System.out.println(name + " release alock");
                System.out.println(name + " waiting block");
                synchronized (block) {
                    System.out.println(name + " get block");
                }
                System.out.println(name + " release block");
            }
        }
    
        // 在线程B中先获取b锁,再获取a锁
        class ThreadB implements Runnable {
            private String name = "ThreadB";
            @Override
            public void run() {
                synchronized (block) {
                    System.out.println(name + " get block");
                    sleepOfSeconds(10);
                }
                System.out.println(name + " release block");
                System.out.println(name + " waiting alock");
                synchronized (alock) {
                    System.out.println(name + " get alock");
                }
                System.out.println(name + " release alock");
            }
        }
    }
    

    线程进入死锁状态时,不会导致CPU持续高负载,实际上当线程进入死锁之后是等待获取对象所被执行,此时CPU是空闲的。

    导致CPU负载持续高的原因是线程进入了死循环,导致CPU持续在工作,此时线程的状态应该是Runnable,而不是Blocked。

    排查Java进程导致CPU持续高的方法

    在Linux环境下,通过如下步骤可以实现对Java进程CPU持续高负载的问题排查:

    1. 通过jps命令找到Java进程ID,并使用top命令确定CPU占用高的进程是否为jps命令找到的Java进程。
    2. 通过ps -mp pid -o THREAD,tid,time命令查看进程的线程列表,找到CPU占用最高的线程ID,并使用printf "%x " tid命令输出线程ID的16进制格式:tid_hex 。
    3. 通过jstack pid |grep tid_hex -A 30查看线程堆栈信息,找到处于Runnable状态的线程中执行的代码片段,就可以分析出对应导致线程死循环的原因了。

    可以将上述命令整理成一个脚本工具,用于临时排查CPU高的问题,详见:https://raw.githubusercontent.com/nuccch/iToolBox/master/shell/show_java_process_thread_stack.sh

    Tomcat的CPU占用高的原因总结

    1. 线程死锁和线程死循环不是一个概念,千万不要弄错。
    2. 通常来讲,对于部署到Tomcat中的应用程序,排除程序代码进入死循环的原因之外,会导致Tomcat进程CPU持续高负载的可能因素是存在大量的TCP连接请求(并发很大)。
    3. 由于应用程序出现堆内存空间不够用导致频繁GC,也会导致CPU使用率高。
    4. 如果应用日志输出非常频繁,也会导致CPU使用率持续高。

    【参考】
    https://www.jianshu.com/p/3160ba8e150d 记一次tomcat cpu占用率过高的问题排查
    http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html 线上应用故障排查之一:高CPU占用

  • 相关阅读:
    字符读取流缓冲区
    Runtime、System类
    字符流和字节流
    将C盘一个文本文件复制到D盘。
    DateDemo
    Collection单列集合 Map双列集合
    泛型
    Collection接口
    String类
    Python代码约定
  • 原文地址:https://www.cnblogs.com/nuccch/p/12535691.html
Copyright © 2020-2023  润新知