记一次CPU持续增长的问题解决

线上服务因功能变更导致CPU占用和网络包传输量持续增长,通过py-spy火焰图定位到__get_onestore_addrs函数,发现由于地址刷新时间减少和多次实例化导致问题。修复后,引入单例模式避免类似问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

线上服务在一次功能变更上线的约一周后, 出现了服务超时问题

直观考虑

Q1: 由于服务本身定位是下游的保障服务, 服务质量受上游流量影响, 对上流的突发大流量请求, 偶尔会有超时情况出现;

A1: 查看上游请求流量, 发现在时间上吻合, 确实存在突增的上游流量, 查看此时流量已经下降, 继续观察超时发生情况;

约几分钟后, 发现超时请求量下降, 但仍然存在。和前几天同时段相比, 有所升高, 先重启服务, 后续定位。

后续定位

考虑到最新的服务变更, 以及上线后约一周才出现的问题。

所以在服务重启后, 持续观察服务的指标情况, 发现服务实例的网络包传输量和cpu占用逐步增长, 网络包传输情况如下:

​根据cpu和网络包的同时增长, 基本可以断定是后台有定时任务不断发送网络请求导致。

考虑到本次功能主要变更为:

使用「触发式缓存刷新」代替「定时的缓存刷新」功能

怀疑大概率与此功能的部分实现有问题有关。

这里使用的简单的redis的pub/sub机制, 可能启动了越来越多的listener, 导致一直刷新, 查看对应代码, 也是做了类似的全局唯一处理。

代码片段如下:

​既然不是, 剩下的就是复杂的业务模块, 一个一个排查不现实, 上工具。

查看cpu时间, py-spy火焰图正合适, 在预上线准备环境, 得到火焰图如下:

<think>嗯,用户问的是Java的jar包运行一段时间后CPU占用率达到200%可能是什么问题。首先,我需要确认用户的使用场景。通常,CPU占用率高可能出现在服务器应用或者长时间运行的后台服务中。用户提到的是jar包,所以很可能是基于Spring Boot或者其他框架打包的应用。 接下来,我得考虑可能的原因。CPU使用率高通常与线程频繁执行计算密集型操作、死循环、锁竞争、频繁GC或者代码逻辑问题有关。用户提到的是运行一段时间后出现,所以可能不是启动时的问题,而是随着时间累积出现的,比如内存泄漏导致GC频繁,或者某个定时任务出了问题。 用户可能是在生产环境遇到这个问题,需要快速定位和解决。他们可能没有详细的监控工具,所以需要一些基本的排查步骤,比如使用top命令查看进程,jstack获取线程转储,或者jstat查看GC情况。 另外,用户可能对Java内部机制不是很熟悉,所以需要用简单易懂的语言解释可能的原因,并提供具体的解决步骤。比如,如何检查线程状态,如何分析GC日志,或者如何优化代码。 还需要注意,CPU使用率200%意味着在多核系统上,两个核心被完全占用,这通常是因为有多个线程在并行执行高负载任务。可能的原因包括线程池配置不当,导致创建过多线程,或者某个循环没有正确的退出条件。 用户可能没有提到日志信息,所以需要建议他们检查应用日志,看是否有异常或错误信息。同时,也要考虑外部依赖,比如数据库查询变慢,导致线程阻塞,进而引起CPU飙升。 最后,需要给出一个结构化的回答,分点列出可能的原因和解决方法,帮助用户逐步排查问题。可能还需要推荐一些工具,比如Arthas,来帮助诊断问题。</think>--- ### **1. 问题本质** Java进程的CPU占用率达到**200%**(在多核系统中,表示两个CPU核心被完全占用),通常由以下原因导致: - **线程密集计算**:代码中存在死循环、未优化的算法或高并发计算任务。 - **资源争用**:线程锁竞争(如`synchronized`、`ReentrantLock`)导致线程频繁阻塞和唤醒。 - **垃圾回收(GC)频繁**:内存泄漏或堆配置不当引发频繁Full GC,消耗CPU资源。 - **外部依赖阻塞**:如慢SQL查询、网络IO阻塞导致线程池资源耗尽。 - **框架或库的Bug**:第三方库(如Spring、Netty)的异常行为导致CPU空转。 --- ### **2. 排查步骤** #### **步骤1:定位高CPU线程** 1. **查看进程CPU占用**: ```bash top -H -p <java_pid> # 查看线程级别的CPU占用(Linux) ``` - 录占用最高的线程ID(十进制转十六进制,用于后续分析)。 2. **生成线程快照**: ```bash jstack <java_pid> > thread_dump.txt # 输出线程堆栈 ``` - 在`thread_dump.txt`中搜索对应线程的十六进制ID(如`nid=0x3e7`),分析其堆栈信息。 #### **步骤2:常见问题模式分析** - **死循环/密集计算**: - 线程堆栈中显示长时间停留在同一方法(如`while(true)`、复杂数学运算)。 - **锁竞争**: - 多个线程状态为`BLOCKED`,且等待同一锁(如`synchronized`代码块)。 - **GC频繁**: - 使用`jstat`监控GC情况: ```bash jstat -gcutil <java_pid> 1000 # 每秒输出GC统计 ``` - 若`FGC`(Full GC次数)持续增长且`FGCT`(Full GC时间)显著,可能存在内存泄漏。 #### **步骤3:代码与依赖检查** - **检查高并发代码**: - 线程池配置是否合理(如核心线程数过大)。 - 是否存在未优化的循环或递归调用。 - **外部依赖分析**: - 数据库慢查询、Redis大Key、HTTP接口超时等可能导致线程阻塞。 - 使用`jstack`观察线程状态是否为`RUNNABLE`(CPU占用)或`WAITING`(IO阻塞)。 --- ### **3. 常见原因与解决方案** #### **(1) 代码死循环或密集计算** - **示例场景**: ```java // 错误示例:未设置退出条件的循环 while (true) { // 密集计算(如未批处理的流处理) } ``` - **解决**: - 优化算法复杂度(如减少嵌套循环)。 - 添加合理的退出条件或休眠(如`Thread.sleep()`)。 #### **(2) 线程锁竞争** - **示例场景**: ```java // 错误示例:粗粒度锁导致并发性能差 public synchronized void process() { // 耗时操作 } ``` - **解决**: - 缩小锁范围(如使用细粒度锁或`ConcurrentHashMap`)。 - 替换为无锁数据结构(如`AtomicInteger`)或读写锁(`ReentrantReadWriteLock`)。 #### **(3) 频繁垃圾回收(GC)** - **现象**: - `jstat`显示`FGC`频繁增长,`OU`(老年代使用量)接近100%。 - 日志中频繁出现`Full GC`。 - **解决**: - 分析堆转储(`jmap -dump:format=b,file=heap.hprof <pid>`),用MAT工具查找内存泄漏。 - 调整堆大小(如`-Xmx4g -Xms4g`)或选择更适合的GC算法(如G1)。 #### **(4) 框架或库的异常行为** - **典型问题**: - Spring Boot Actuator端点被频繁调用。 - Netty的EventLoop线程因任务堆积导致CPU满载。 - **解决**: - 检查框架配置(如Actuator的安全限制)。 - 使用`AsyncProfiler`或`Arthas`分析热点方法。 #### **(5) 外部依赖阻塞** - **示例场景**: - 数据库查询未加索引,导致单次查询耗时过长。 - HTTP客户端未设置超时,线程池被占满。 - **解决**: - 优化SQL(添加索引、分页查询)。 - 为外部调用设置超时(如`Feign.client.config.default.readTimeout=5000`)。 --- ### **4. 工具推荐** 1. **Arthas**: - 动态跟踪Java方法调用,定位CPU热点: ```bash trace com.example.Service * '#cost > 100' # 跟踪耗时超过100ms的方法 ``` 2. **AsyncProfiler**: - 生成火焰图,直观展示CPU占用分布: ```bash ./profiler.sh -d 30 -f flamegraph.html <java_pid> ``` 3. **VisualVM**: - 监控线程状态、内存和GC活动。 --- ### **5. 示例场景** **问题**:某电商系统促销期间CPU飙升至200%,接口响应变慢。 **分析**: 1. `top`发现两个线程CPU占用90%以上。 2. `jstack`显示线程处于`RUNNABLE`状态,堆栈指向`JSON.parseObject()`方法。 3. 代码中存在循环解析大JSON文件且未启用流式解析。 **解决**: - 改用`JsonReader`流式解析。 - 增加JVM堆大小:`-Xmx8g -Xms8g`。 - 优化JSON数据结构,减少嵌套层级。 --- ### **6. 预防措施** 1. **代码审查**:避免死循环、未优化的算法和粗粒度锁。 2. **性能测试**:压测阶段使用JMeter模拟高并发,观察CPU和GC。 3. **监控告警**:集成Prometheus + Grafana监控JVM指标。 4. **合理配置**:根据负载调整线程池大小和JVM参数。 --- ### **总结** Java进程CPU占用200%的核心原因是 **线程长期占用CPU资源未释放**,可能由代码逻辑缺陷、锁竞争或GC频繁引发。通过`jstack`定位高CPU线程、结合`jstat`和堆转储分析,可快速定位问题根源。优化代码逻辑、调整JVM参数和合理使用工具是解决问题的关键。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值