Spring Boot API处理错误后CPU无法恢复至空闲状态
针对Spring Boot API异常请求后CPU无法恢复的解决方案建议
问题概述
我们的Spring Boot API每月会出现一次性能下降,仅能通过重启应用恢复。经排查,向抛出异常的特定控制器发送大量HTTP请求后,CPU使用率无法回落至正常水平。复现步骤:
- 运行多实例含无限循环的bash脚本,发送触发异常的HTTP请求
- 等待服务器CPU使用率达100%
- 等待15分钟
- 停止bash脚本的无限循环
停止请求后,通过Java VisualVM观察到CPU仍维持在25%左右,堆直方图显示io.micrometer.core.instrument.Tag[]实例数最多。已尝试升级Spring Boot版本、设置系统变量LatencyUtils.useActualTime=false、手动执行垃圾回收等操作,均未解决问题。期望无请求时CPU/内存能恢复至空闲水平。
解决方案建议
1. 排查Micrometer Tag相关的内存泄漏与CPU负载
- 检查异常处理流程中的Tag创建逻辑:若异常监控逻辑每次触发都新建
Tag[]实例而非复用,高并发下会产生大量对象,若存在未释放的引用,会导致内存堆积,进而引发后台统计线程持续占用CPU。可临时复用固定Tag实例进行测试,验证CPU是否回落 - 验证异常相关指标配置:若针对该控制器的异常请求设置了高频更新的指标,即使请求停止,Micrometer的后台统计线程可能仍在处理残留数据。可临时禁用该接口的异常监控指标,复现测试观察CPU变化
- 检查Tag值的合理性:避免动态生成唯一值、超长字符串作为Tag值,这类值会导致Tag无法被缓存复用,大量实例堆积触发频繁GC或后台线程负载过高
2. 分析异常处理的线程与资源释放逻辑
- 导出线程栈快照(使用
jstack命令):排查是否有大量线程卡在异常处理逻辑中,比如自定义线程池的任务堆积、@ControllerAdvice中存在循环调用或未释放的资源(如数据库连接、文件句柄) - 检查异步异常处理逻辑:若异常处理中使用了异步任务,高并发下可能造成任务队列堆积,即使请求停止,线程池仍在处理残留任务,导致CPU持续占用
3. 定位Tag[]实例的引用链
- 使用
jmap -histo或Java VisualVM的内存分析功能,查看io.micrometer.core.instrument.Tag[]实例的引用持有者,确认是Micrometer的指标注册表、自定义监控逻辑还是其他组件未释放引用,精准定位泄漏点
4. 优化Micrometer配置与版本
- 启用Tag缓存:部分Micrometer版本支持Tag实例复用,可配置
management.metrics.tags.cache.enabled=true(需对应Spring Boot版本支持),减少对象创建 - 调整指标采样频率:降低异常相关指标的采样频率,减少后台统计线程的负载
- 单独升级Micrometer:即使升级了Spring Boot,可能内嵌的Micrometer仍存在已知问题,可单独将Micrometer核心依赖升级至最新稳定版,排查是否修复了相关内存泄漏/CPU占用问题
5. 流量控制与异常逻辑优化
- 给触发异常的接口添加流量限制:在网关层或控制器中设置QPS阈值,避免短时间内大量请求耗尽系统资源
- 简化异常返回逻辑:异常处理时避免执行复杂的统计、IO操作,减少资源消耗,确保线程能快速释放
内容的提问来源于stack exchange,提问作者shivkumar98
相关产品推荐
相关产品推荐

