更新时间:2024-11-08 GMT+08:00
如何使用APM Profiler定位性能问题
APM Profiler 是一种持续性能剖析工具,可以帮助开发者准确找到应用程序中消耗资源最多的代码位置。
前提条件
- APM Agent 已接入,操作方法参见开始监控JAVA应用。
- Profiler功能已开启,操作方法参见Profiler性能分析。
- 登录应用性能管理控制台。
如何查询并解决CPU升高问题
- 在左侧导航栏选择“应用监控 > 指标”。
- 在界面左侧树单击待查看基础监控环境后的。
- 单击“基础监控”,切换至基础监控页签,监控项选择“JVM监控”。
图1 查看JVM监控
- 找到“cpu(%)”,发现CPU持续高达80%以上。
图2 cpu(%)
- 单击“Profiler性能分析”,切换至Profiler性能分析页签。
- 单击“性能分析”,Profiler性能分析页面,类型选择“CPU Time”。
图3 Profiler火焰图
- 分析火焰图数据 从火焰图中可以看到,java.util.LinedList.node(int) 方法占用了 66% 的 CPU,而相应的业务代码方法是 countPages(List)。
图4 Profiler火焰图分析
- 分析业务代码,结合代码可以发现该方法countPages(List)是对入参集合list进行下标遍历,而通过火焰图运行时数据发现,传入的是 LinkedList,而LinkedList底层数据结构是链表,通过下标遍历效率会非常差。
图5 代码分析
- 修复代码,将list的遍历算法从普通的下标for循环改为增强的for循环。
图6 修复代码
- 优化后,重复步骤4-步骤5,发现CPU使用率<1%。
图7 优化后CPU(%)
如何查询并解决内存升高问题
前提条件:开启测试程序,同时设定heap的大小为2g(-Xms2g -Xmx2g)。
- 在左侧导航栏选择“应用监控 > 指标”。
- 在界面左侧树单击待查看基础监控环境后的。
- 单击“基础监控”,切换至基础监控页签,监控项选择“ GC监控”,⾮常频繁的进⾏gc操作。
图8 查看GC监控
- 监控项选择“JVM监控”,查看JVM监控。
图9 查看JVM监控
- 单击“Profiler性能分析”,切换至Profiler性能分析页签。
- 单击“性能分析”,Profiler性能分析页面,实例选择“Allocated Memory”。根据右侧Self排序排查,找到分配内存最多的方法。
图10 内存火焰图
- 查看代码,发现LargeEnum是个枚举类,定义了大量的常量。由于枚举类的方法 values() 底层是通过数组clone实现的,即每次调用values()方法,底层会复制一个枚举数组,所以会导致频繁分配堆内存,频繁GC。
图11 查看代码
- 问题修复,将values定义为一个常量,避免频繁调用enum.values()。
图12 问题修复
- 重复步骤3-步骤6,发现GC次数大幅下降,并且火焰图中以找不到enum.values()内存分配。
图13 优化后GC监控
图14 优化后性能分析火焰图
如何查询并解决接口响应慢问题
- 在左侧导航栏选择“应用监控 > 指标”。
- 在界面左侧树单击待查看基础监控环境后的。
- 单击“接口调用”,切换至接口调用页签。通过APM的接口调用功能发现接口响应慢,平均响应时间80s左右。
图15 接口调用
- 单击“Profiler性能分析”,切换至Profiler性能分析页签。
- 单击“性能分析”,Profiler性能分析页面,实例选择“Latency”, 输入接口所在的方法。
图16 性能分析
- 排查调用栈,寻找耗时的方法。如下图,NegativeWorkService#handle中executeUpdate()方法耗时最多。
图17 排查调用栈
- 排查NegativeWorkService#handle方法,发现根因是循环内执行数据库插入操作。
图18 排查NegativeWorkService#handle方法
- 问题修复,改为批量插入数据。
图19 问题修复
- 观察接口调用平均响应时间,从80s减少到0.2s。
图20 优化后查询接口调用平均响应时间