当前位置: 首页 > news >正文

[Java实战]Spring Boot服务CPU 100%问题排查:从定位到解决

Spring Boot服务CPU 100%问题排查:从定位到解决

1. 引言

当Spring Boot服务出现CPU占用率100%时,系统性能会急剧下降,甚至导致服务不可用。本文将通过真实代码案例,详细讲解如何快速定位问题根源,并提供解决方案。无论是死循环、线程阻塞还是资源泄漏,本文帮你一网打尽!

2. 问题现象

  • 服务器CPU使用率持续100%
  • 接口响应时间变长或超时
  • 通过监控工具(如Grafana、Arthas)发现异常线程

3. 排查步骤

3.1 编写测试代码

在这里插入图片描述

3.2 运行代码效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 定位高CPU进程

使用top命令快速找到占用CPU最高的Java进程:

top -c   # 显示完整命令,找到PID

终端输入命令:top -c,找到高cpu进程PID:420364

在这里插入图片描述

3.4 定位高CPU线程

通过top -Hp <PID>查看进程内线程的CPU使用情况,记录线程ID(需转换为十六进制):

#查找进程中的线程
top -Hp <PID>

top -Hp 420364
 # 假设线程ID为433127 → 69be7 输出为16进制

printf "%x\n" 433127 


[root@localhost java]# printf "%x\n" 433127 
69be7

终端输入命令:top -Hp 420364,定位高cpu线程:433127

线程ID转换为16进制:69be7

生成堆栈信息:jstack 420364 > stack.log 生成时PID:420364

通过堆栈信息分析:grep -A 20 ‘nid=0x69be7’ stack.log nid=0x69be7为线程id

根据信息对比原代码可找到问题

在这里插入图片描述

3.5分析线程堆栈

使用jstack捕获线程堆栈并分析:

#生成线程堆栈
jstack <PID> > stack.log

jstack 420364 > stack.log
 # 搜索特定线程
grep -A 20 'nid=0x69be7' stack.log 

在这里插入图片描述

在这里插入图片描述

排查结果及源代码问题对比:类32行运行结果导致cpu 100%

在这里插入图片描述

3.5 Arthas 工具定位

以上是根据Java自带的jstack 工具定位, Arthas 工具也能定位排查。定位思路和上面步骤一致

#安装下载启动
curl -O https://arthas.aliyun.com/arthas-boot.jar

java -jar arthas-boot.jar

启动选择需要定位项目:2

在这里插入图片描述

直接输入查看高cpu线程: thread

在这里插入图片描述

查看堆栈信息找到代码问题: thread 29 (线程ID)

在这里插入图片描述

如果想看实时面板信息: dashboard

在这里插入图片描述

4. 常见原因与代码示例

4.1 死循环(While循环未正确退出)

问题代码

@Service
public class CpuProblemController {
    public void deadLoop() {
        while (true) {  // 无条件退出,导致死循环
            // 业务逻辑(如未正确更新循环条件)
        }
    }
}

解决方案

@Service
public class CpuProblemController {
    private volatile boolean running = true;  // 添加退出标志
    
    public void deadLoop() {
        while (running) {  // 通过控制running变量退出循环
            // 业务逻辑
        }
    }
    
    @PreDestroy
    public void stop() {
        running = false;  // 服务关闭时触发退出
    }
}

测试完整类:

package com.example.controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
@RequestMapping("/pro")
public class CpuProblemController {
    // 添加退出标志
    private volatile boolean running = true;

    // 创建线程池
    private final ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    /**
     * 死循环:模拟 CPU 占满
     */
    @PostMapping("/deadLoop")
    public void deadLoop() {
        // 启动多个线程,每个线程执行死循环
        for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) {
            executorService.submit(() -> {
                while (running) {
                    // 死循环,模拟高 CPU 使用率
                    System.out.println("=======死循环=========");
                    // 加入计算密集型任务
                    for (int j = 0; j < 1000000; j++) {
                        Math.sin(j); // 计算密集型操作
                    }
                }
            });
        }
    }

    @PreDestroy
    public void stop() {
        // 服务关闭时触发退出
        running = false;
        executorService.shutdownNow(); // 关闭线程池
    }
}

4.2 不合理递归(栈溢出与CPU飙升)

问题代码

public class RecursionController {
    public int faultyRecursion(int n) {
        return n + faultyRecursion(n - 1);  // 无终止条件 → 无限递归
    }
}

解决方案

public class RecursionController {
    public int safeRecursion(int n) {
        if (n <= 0) {  // 添加递归终止条件
            return 0;
        }
        return n + safeRecursion(n - 1);
    }
}

4.3 线程池未正确关闭(资源泄漏)

问题代码

@RestController
public class ThreadController {
    private ExecutorService executor = Executors.newFixedThreadPool(10);

    @GetMapping("/task")
    public String submitTask() {
        executor.submit(() -> {
            while (true) {  // 线程池任务未终止
                // 长时间运行的任务
            }
        });
        return "Task submitted!";
    }
    
    // 未重写destroy方法关闭线程池 → 线程泄漏
}

解决方案

@RestController
public class ThreadController {
    private ExecutorService executor = Executors.newFixedThreadPool(10);

    @GetMapping("/task")
    public String submitTask() {
        executor.submit(() -> {
            while (!Thread.currentThread().isInterrupted()) {  // 检查中断状态
                // 可中断的任务逻辑
            }
        });
        return "Task submitted!";
    }
    
    @PreDestroy
    public void shutdown() {
        executor.shutdownNow();  // 服务关闭时终止线程池
    }
}

4.4 锁竞争(线程阻塞与自旋)

问题代码

public class LockCompetitionController {
    private final Object lock = new Object();
    
    public void highCpuMethod() {
        synchronized (lock) {
            // 长时间持有锁(如复杂计算或IO操作)
            try {
                Thread.sleep(10000);  // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

解决方案

public class LockCompetitionDemo {
    private final Object lock = new Object();
    
    public void optimizedMethod() {
        // 缩小锁范围,只锁必要部分
        heavyCalculation();  // 将非线程安全操作移到锁外
        
        synchronized (lock) {
            // 仅保护共享资源
        }
    }
    
    private void heavyCalculation() {
        // 复杂计算逻辑
    }
}

4.5 频繁GC(内存泄漏导致CPU飙升)

问题代码

public class MemoryLeakDemo {
    private static List<byte[]> cache = new ArrayList<>();
    
    @GetMapping("/leak")
    public void leakMemory() {
        while (true) {
            cache.add(new byte[1024 * 1024]);  // 不断添加数据 → 触发Full GC
        }
    }
}

解决方案

public class MemoryLeakDemo {
    private static final int MAX_CACHE_SIZE = 100;
    private static List<byte[]> cache = new ArrayList<>();
    
    @GetMapping("/safe")
    public void safeMethod() {
        if (cache.size() >= MAX_CACHE_SIZE) {
            cache.clear();  // 定期清理缓存
        }
        cache.add(new byte[1024 * 1024]);
    }
}

5. 总结与预防

  1. 代码审查:重点关注循环、递归、线程池和锁的使用。
  2. 压测与监控:使用JMeter模拟高并发,通过Arthas实时监控CPU。
  3. 防御式编程:对递归添加终止条件,对循环添加超时机制。
  4. 工具辅助:利用jvisualvmasync-profiler进行性能分析。

希望这篇文章对你有所帮助!如果觉得不错,别忘了点赞收藏哦!

相关文章:

  • JS - 重点JS方法温故而知新
  • ROS2 系统架构
  • Linux调度器 --- 负载均衡的存在的问题
  • AI 助力医学统计:复杂临床数据处理的 “救星”
  • 【Oracle】19c数据库控制文件多路径配置
  • Docker 容器指标搜集工具cAdvisor
  • 一款基于Python的从常规文档里提取图片的简单工具开发方案
  • 用 Python 进行比特币数据分析:从入门到实战
  • k8s 配置两个deployment主机级别互斥部署
  • 硬件驱动——51单片机:寄存器、LED、动态数码管
  • Google Cloud Run 如何实现无服务器(Serverless)部署?
  • 受控组件非受控组件
  • 论文阅读:Deep Hybrid Camera Deblurring for Smartphone Cameras
  • 【工作记录】pytest使用总结
  • 深度学习中的并行策略:数据并行、流水并行与张量并行
  • DR-CAN 卡尔曼滤波笔记
  • Python库安装报错解决思路以及机器学习环境配置详细方案
  • 机器学习中说的正向传递和反向传递是什么意思
  • NFS网络文件共享服务
  • mysql-8.0.40-1.el7.x86_64.rpm Linux MySQL 保姆级详细安装教程(2025版)
  • 住建部:推进“好房子、好小区、好社区、好城区”四好建设
  • 离休干部周惠梅逝世,曾从事多年地下革命工作
  • 国家发改委:系统谋划7方面53项配套举措,推动民营经济促进法落地见效
  • 上海蝉联全国中小企业发展环境评估综合排名第一
  • 上昆“学馆制”10年,完成300出折子戏和20台大戏传承
  • 发射后失联,印度地球观测卫星发射任务宣告失败