ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

一次现网定位:java cpu占用率过高

2021-07-06 12:31:26  阅读:135  来源: 互联网

标签:现网 java spring 任务 线程 ScheduledExecutorService registrar 占用率


问题描述

spring boot开发的应用部署到环境上后,没有任何业务访问,CPU利用率长期100%。

定位思路

初步判断

大量异步任务导致CPU占用率搞

排查

排查代码中使用ExcutorService的地方,及异步任务(spring的@Scheduled)。
发现有使用@Scheduled,根据配置和实际的业务处理、异步任务打印的日志,理论上不会出现cpu100%,故排除掉。

深入定位

找到CPU占用率高的线程

  1. 找到java进程,16
    在这里插入图片描述
  2. 找到cpu占用率高的线程,top -H -p 16
    在这里插入图片描述
  3. 找到线程的堆栈,转换线程号printf %x 38,查询堆栈jstack 16| grep nid=0x26 -A 10
    在这里插入图片描述

排查结果

从堆栈信息中看不到问题代码,但是可以知道是异步任务导致的问题。
线程池是pool-1-thread-xx的,一般都是用户自己创建的,因为开源组件一般都不会使用默认的线程池名。

下一步思路

多次快速的查询堆栈,看能否捕捉到问题线程,应用层(我们自己的代码)的堆栈信息。 --失败
可见不是应用层处理量大的问题。
可疑点:ThreadPoolExecutor获取任务时没有阻塞

确认可疑点

将断点打到jdk的ThreadPoolExecutor#getTask方法,发现走的分支是圈红的代码。
keepAliveTime为默认值0,所以每次获取任务时没有阻塞,到这里问题根因已经找到了。
队列里面存的是定时任务,重复执行的;因为核心线程数设置的为0,所以走的是圈红的分支,导致不断的轮训任务而不会阻塞

在这里插入图片描述

问题根因追踪:为什么spring定时任务使用的线程池核心线程数为0

使用@schedule注解的bean会被spring的ScheduledAnnotationBeanPostProcessor处理。
该后置处理会分析bean中@schedule注解的方法,并转换成ScheduledTask进行调度处理。
ScheduledAnnotationBeanPostProcessorhi监控spring容器完成refresh的事件,并创建调度器开始调度任务
代码有删减

    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext() == this.applicationContext) {
            this.finishRegistration();
        }
    }
    private void finishRegistration() {
        if (this.scheduler != null) {
            this.registrar.setScheduler(this.scheduler);
        }
        # 设置任务调度器
        this.registrar.setTaskScheduler((TaskScheduler)this.resolveSchedulerBean(this.beanFactory, TaskScheduler.class, false));
        this.registrar.setTaskScheduler((TaskScheduler)this.resolveSchedulerBean(this.beanFactory, TaskScheduler.class, true));
        # 设置线程池
        this.registrar.setScheduler(this.resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, false));
        # 最终在这里完成了线程池的设置,从beanfactory里面获取ScheduledExecutorService类型的bean
        this.registrar.setScheduler(this.resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, true));
        this.registrar.afterPropertiesSet();
    }

我们设置ScheduledExecutorService的代码,因为max字段是protected,spring无法访问,所以该字段没有被设置,默认值为0.
在这里插入图片描述
到这里问题已得到验证及解决。

ScheduledExecutorService核心线程为0带来的影响

标签:现网,java,spring,任务,线程,ScheduledExecutorService,registrar,占用率
来源: https://blog.csdn.net/shuxiaohua/article/details/118515970

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有