不经意的两行代码把CPU使用率干到了90%+

本文涉及的产品
可观测可视化 Grafana 版,10个用户账号 1个月
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 使用arthas定位隐藏的异常

背景介绍

某同学反映某个应用ECS CPU使用率90%+,希望分析下原因。

该应用使用schedulerx来做定时任务执行,每隔一小时执行一次,每次执行5分钟左右,执行任务期间CPU使用率90%+。

问题现象

ECS配置是4c8g,从上图来看系统负载已经非常高了。

分析过程

寻找热点代码

arthas profiler比较适用CPU使用率持续较高的场景。通过对热点火焰图的分析,NoSuchMethodException异常相关代码占用了很多CPU时间。

上图红框中NoSuchMethodException展开后如下图:

分析异常

ClassUtils

从上图可以看出org.springframework.util.ClassUtils.getStaticMethod调用了Class.getMethod,Class.getMethod抛出了NoSuchMethodException,代码如下。

为了进一步定位问题,需要知道ClassUtils.getStaticMethod方法的入参:

从上图看出ClassUtils.getStaticMethod方法入参分别是:

**clazz:java.util.Date;_methodName:_valueOf;args[0]:**java.sql.Timestamp。上面图片只是截取了一部分,其中methodName还有of、from。

ObjectToObjectConverter

调用ClassUtils.getStaticMethod的地方是org.springframework.core.convert.support.ObjectToObjectConverter.determineFactoryMethod:

调用ObjectToObjectConverter.determineFactoryMethod的地方是ObjectToObjectConverter.getValidateMember:

虽然java.sql.Timestamp是java.util.Date的子类,但是从上面代码可以看出进行了很多次无效的调用。

定位业务代码

定位业务代码

为了更准确的定位相关业务代码,我们需要知道抛出NoSuchMethodException的线程栈,可以使用arthas stack,从线程栈我们可以知道在【哪个类哪个方法哪行】发出的调用。

stack org.springframework.util.ClassUtils getStaticMethod 'returnObj==null'

分析业务代码

在我们没有源代码的情况,我们可以使用arthas jad反编译定位到的类,进而分析业务代码,到这里就可以具体定位到问题了。

gmt_created、gmt_modified在实体类中的定义:

异常场景回顾

  1. 查询数据库,数据库返回ResultSet对象
  2. 遍历ResultSet,将ResultSet每一行映射到相应的业务实体类
  1. 实例化业务实体类,根据ResultSet.getMetaData()获取每一列的值并将该值set到实体类对应属性上
  1. 在将gmt_created、gmt_modified解析为java.sql.Timestamp类实例,接着使用ObjectToObjectConverter将java.sql.Timestamp转换为java.util.Date的时候抛出了NoSuchMethodException

异常场景复现示例代码

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.List;
public class TimestampToDateTest {
    public static void main(String[] args){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost/world?useSSL=false&serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        String sql = "select now() as gmt_created,now() as gmt_modified";
    RowMapper rowMapper = new BeanPropertyRowMapper(TimestampToDate.class);
        List<TimestampToDate> list = jdbcTemplate.query(sql,rowMapper);
        for(TimestampToDate timestampToDate : list){
            System.out.println(timestampToDate.getGmtCreated().getClass().getName());
        }
    }
}
import java.util.Date;
public class TimestampToDate {
    private Date gmtCreated;
    private Date gmtModified;
    public Date getGmtCreated() {
        return gmtCreated;
    }
    public void setGmtCreated(Date gmtCreated) {
        this.gmtCreated = gmtCreated;
    }
    public Date getGmtModified() {
        return gmtModified;
    }
    public void setGmtModified(Date gmtModified) {
        this.gmtModified = gmtModified;
    }
}

解决办法

数据库表中gmt_created、gmt_modified类型与实体类中对应字段类型的定义保持一致,可以解决异常。

延申阅读

通过提高BeanPropertyRowMapper相关逻辑的缓存命中率可以进一步优化性能,如提前将转换逻辑放到GenericConversionService类的converters中:

BeanPropertyRowMapper rowMapper = new BeanPropertyRowMapper(TimestampToDate.class);
DefaultConversionService conversionService = (DefaultConversionService)rowMapper.getConversionService();
conversionService.addConverter(Timestamp.class, Date.class, new Converter<Timestamp, Date>() {
  @Override
  public Date convert(Timestamp source) {
    return source;
  }
});

另外可以通过自定义RowMapper来提高性能,因为BeanPropertyRowMapper并不是高性能的一种实现:

参考资料

Java, JDBC, and MySQL Types

目录
相关文章
|
6月前
|
SQL 数据管理 网络安全
数据管理DMS操作报错合集之DMS的CPU使用率达到100%,如何解决
数据管理DMS(Data Management Service)是阿里云提供的数据库管理和运维服务,它支持多种数据库类型,包括RDS、PolarDB、MongoDB等。在使用DMS进行数据库操作时,可能会遇到各种报错情况。以下是一些常见的DMS操作报错及其可能的原因与解决措施的合集。
|
6月前
|
Web App开发 Java 测试技术
ChaosBlade常见问题之演练场景页面乱码cpu使用率图片显示不出来如何解决
ChaosBlade 是一个开源的混沌工程实验工具,旨在通过模拟各种常见的硬件、软件、网络、应用等故障,帮助开发者在测试环境中验证系统的容错和自动恢复能力。以下是关于ChaosBlade的一些常见问题合集:
112 0
|
6月前
|
监控 算法 Linux
【C/C++ 实用工具】CPU使用率监控工具对比
【C/C++ 实用工具】CPU使用率监控工具对比
156 0
|
6月前
|
监控 Java 索引
cpu使用率过高和jvm old占用过高排查过程
cpu使用率过高和jvm old占用过高排查过程
163 2
|
Shell
我来教你如何将cpu使用率up起来(shell脚本[含注释])
我来教你如何将cpu使用率up起来(shell脚本[含注释])
516 0
|
Java Linux
linux中找到最耗CPU的那段Java代码
linux中找到最耗CPU的那段Java代码
|
2月前
|
调度
CPU调度器实现提示:针对特定体系结构代码【ChatGPT】
CPU调度器实现提示:针对特定体系结构代码【ChatGPT】
|
4月前
|
运维 监控 Linux
解决CPU与带宽高使用率问题:深入分析与应对策略
引言:性能问题的诊断与优化 在运维工作中,操作系统性能问题如影随形,典型代表是CPU使用率高和带宽使用率高的问题,它们直接影响应用的性能和响应时间。这篇记录将逐个分析这两个问题的产生原因和解决方法。
解决CPU与带宽高使用率问题:深入分析与应对策略
|
4月前
|
机器学习/深度学习 TensorFlow API
Keras是一个高层神经网络API,由Python编写,并能够在TensorFlow、Theano或CNTK之上运行。Keras的设计初衷是支持快速实验,能够用最少的代码实现想法,并且能够方便地在CPU和GPU上运行。
Keras是一个高层神经网络API,由Python编写,并能够在TensorFlow、Theano或CNTK之上运行。Keras的设计初衷是支持快速实验,能够用最少的代码实现想法,并且能够方便地在CPU和GPU上运行。
|
5月前
|
并行计算 异构计算 Python
python代码torch.device("cuda:0" if torch.cuda.is_available() else "cpu")是什么意思?
【6月更文挑战第3天】python代码torch.device("cuda:0" if torch.cuda.is_available() else "cpu")是什么意思?
548 4