背??景介绍有同学反馈某应用ECSCPU使用率90%+,希望分析原因。应用使用schedulerx进行定时任务执行,每小时执行一次,每次5分钟左右,任务执行期间CPU使用率90%+。问题症状图1ECS监控指标ECS配置为4c8g。从上图来看,系统负载已经很高了。分析过程中,热点代码arthasprofiler比较适合CPU使用率持续高的场景。通过热火焰图分析,与NoSuchMethodException异常相关的代码占用了大量CPU时间。图2热点火焰图中红框中的NoSuchMethodException展开如下:图2异常火焰图分析异常ClassUtils从上图可以看出org.springframework.util.ClassUtils.getStaticMethod调用了Class.getMethod,而Class.getMethod抛出一个NoSuchMethodException,代码如下。图3ClassUtils.getStaticMethod为了进一步定位问题,需要知道ClassUtils.getStaticMethod方法的入参:图4arthaswatch从上图可以看出ClassUtils.getStaticMethod方法的入参是:**clazz:java.util.Date;_方法名称:_valueOf;args[0]:**java.sql.时间戳。上图只是截取了一部分,其中methodName还有of和from。ObjectToObjectConverter调用ClassUtils.getStaticMethod的地方是org.springframework.core.convert.support.ObjectToObjectConverter.determineFactoryMethod:图5ObjectToObjectConverter.determineFactoryMethod调用ObjectToObjectConverter.determineFactoryMethod是ObjectToObjectConverter.getTimeMember.getValidateMember:图6是java.util的子类。Date,但是从上面的代码可以看出,进行了很多无效的调用。定位业务代码为了更准确的定位相关业务代码,我们需要知道抛出NoSuchMethodException异常的线程栈。我们可以使用arthas堆栈。从线程堆栈中,我们可以知道在[哪个类,哪个方法,哪一行]中进行了调用。stackorg.springframework.util.ClassUtilsgetStaticMethod'returnObj==null'图7arthasstack分析业务代码在没有源码的情况下,可以使用arthasjad反编译定位到的类,然后分析业务代码,herewego可以具体定位问题。图8实体类中定义业务代码gmt_created和gmt_modified:图9业务实体类异常场景Review查询数据库,数据库返回ResultSet对象。遍历ResultSet,将ResultSet的每一行映射到对应的业务实体类,实例化业务实体类,根据ResultSet.getMetaData()获取每一列的值并将该值设置为实体类的对应属性,以及然后将gmt_created和gmt_modified解析为java.sql.Timestamp类实例,然后使用ObjectToObjectConverter将java.sql.Timestamp转换为java.util.Date并抛出NoSuchMethodException。重现异常场景示例代码:图10测试代码图11测试代码方案扩展阅读可以通过提高BeanPropertyRowMapper相关逻辑的缓存命中率进一步优化性能,比如将转换逻辑提前放在GenericConversionService类的converters中:图12优化逻辑也可以通过自定义RowMapper来提升性能,因为BeanPropertyRowMapper并不是高性能的实现:图13BeanPropertyRowMapper
