Linux 下 .NET 程序 CPU 异常占用排查记录

Linux 下 .NET 程序 CPU 异常占用排查记录

问题背景

目标程序位于:/home/website_net/dev_web

现场现象:dotnet 进程 CPU 占用异常偏高,示例进程信息如下:

text 复制代码
PID      USER   PR NI   VIRT    RES   SHR S  %CPU %MEM     TIME+ COMMAND
1660968  root   20  0   24.4g 268004 69800 S 100.3  1.6  54:20.51 dotnet

初步判断:该现象通常不是正常状态,常见原因包括忙循环、异常风暴、线程池异常扩容、锁竞争、自旋、GC 压力过大等。


当前运行环境

执行命令:

bash 复制代码
./dotnet --info

环境信息如下:

text 复制代码
.NET SDK:
 Version:   6.0.405
 Commit:    27ab36058b

运行时环境:
 OS Name:     debian
 OS Version:  11
 OS Platform: Linux
 RID:         debian.11-arm64
 Base Path:   /home/website_net/netsdk/dotnet/sdk/6.0.405/

Host:
  Version:      6.0.13
  Architecture: arm64

.NET SDKs installed:
  6.0.405 [/home/website_net/netsdk/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 6.0.13 [/home/website_net/netsdk/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 6.0.13 [/home/website_net/netsdk/dotnet/shared/Microsoft.NETCore.App]

结论:

  • 当前机器是 Debian 11 arm64
  • 当前 SDK 是 .NET 6.0.405
  • 当前 Host Runtime 是 .NET 6.0.13
  • 安装诊断工具时,必须选择兼容 .NET 6 的版本

为什么安装 dotnet-trace 会失败

执行命令:

bash 复制代码
./dotnet tool install --global dotnet-trace

报错:

text 复制代码
error NU1202: 包 dotnet-trace 9.0.661903 与 net6.0 不兼容。
包 dotnet-trace 9.0.661903 支持: net8.0

原因:

  • 默认安装到了最新的 dotnet-trace 9.x
  • 9.x 版本要求 net8.0
  • 当前系统只有 .NET 6,因此出现 NU1202 不兼容错误

结论:

  • 不是网络问题
  • 不是工具名错误
  • 是工具版本与本机运行时版本不匹配

正确安装方式

方式一:全局安装

先设置环境变量:

bash 复制代码
export DOTNET_ROOT=/home/website_net/netsdk/dotnet
export PATH=$DOTNET_ROOT:$PATH
export PATH=$PATH:/root/.dotnet/tools

安装兼容 .NET 6 的版本:

bash 复制代码
dotnet tool install --global dotnet-trace --version 6.*
dotnet tool install --global dotnet-stack --version 6.*
dotnet tool install --global dotnet-counters --version 6.*

验证:

bash 复制代码
dotnet-trace --version
dotnet-stack --version
dotnet-counters --version

方式二:安装到固定目录

如果不希望使用全局目录,可以安装到指定路径:

bash 复制代码
dotnet tool install dotnet-trace --tool-path /home/website_net/netsdk/dotnet/tools --version 6.*
dotnet tool install dotnet-stack --tool-path /home/website_net/netsdk/dotnet/tools --version 6.*
dotnet tool install dotnet-counters --tool-path /home/website_net/netsdk/dotnet/tools --version 6.*

验证:

bash 复制代码
/home/website_net/netsdk/dotnet/tools/dotnet-trace --version
/home/website_net/netsdk/dotnet/tools/dotnet-stack --version
/home/website_net/netsdk/dotnet/tools/dotnet-counters --version

说明:

  • 如果 6.* 解析失败,可以改为指定精确的 6.0.x 版本
  • 在无法访问 nuget.org 的环境下,需要改用镜像源或离线包方式安装

推荐排查顺序

1. 先看是否某些线程打满

bash 复制代码
top -H -p 1660968

目的:

  • 确认是否单个或少数线程持续高 CPU
  • 记录高 CPU 线程的 TID,便于后续分析

2. 看运行时指标

bash 复制代码
dotnet-counters monitor -p 1660968 System.Runtime

重点关注:

  • cpu-usage
  • gc-heap-size
  • gen-2-gc-count
  • exception-count
  • threadpool-thread-count
  • monitor-lock-contention-count

判断方向:

  • exception-count 很高:可能是异常反复抛出
  • threadpool-thread-count 快速增长:可能线程池饥饿或阻塞
  • monitor-lock-contention-count 高:可能锁竞争严重
  • GC 指标异常:可能对象分配过快、GC 压力大

3. 抓 CPU 采样,定位热点方法

bash 复制代码
dotnet-trace collect -p 1660968 --profile cpu-sampling --duration 00:00:30 -o cpu.nettrace

目的:

  • 找出 CPU 时间主要耗在哪些托管方法上
  • 定位是否存在热点循环、频繁序列化、频繁日志、频繁 LINQ、正则、JSON 处理等问题

4. 连续查看托管线程栈

bash 复制代码
dotnet-stack report -p 1660968

建议:

  • 每隔 2 到 3 秒执行一次
  • 如果多次看到同一批方法总在栈顶,基本可以判断热点位置

5. 如果怀疑 native 或内核层问题

bash 复制代码
perf top -p 1660968

或者:

bash 复制代码
perf record -g -p 1660968 -- sleep 30
perf report

目的:

  • 看是否卡在 libcfutex、锁、自旋、系统库调用等位置

6. 如果怀疑系统调用风暴

bash 复制代码
strace -fp 1660968 -tt -T

目的:

  • 判断是否在高频执行 futexepoll_waitreadwrite 等系统调用

常见根因清单

  1. while 循环没有 sleep,形成忙等
  2. 定时任务或 BackgroundService 调度过于频繁
  3. 异常被捕获后无限重试,导致 CPU 被异常处理吃满
  4. 线程池饥饿,线程不断补充
  5. 锁竞争导致线程自旋
  6. 短周期大量分配对象,触发频繁 GC
  7. 日志打印过于频繁,尤其在循环内输出
  8. 某些缓存失效后反复全量扫描或重复计算

建议的最小排查命令清单

如果工具已安装,建议按下面顺序执行:

bash 复制代码
top -H -p 1660968
dotnet-counters monitor -p 1660968 System.Runtime
dotnet-stack report -p 1660968
dotnet-trace collect -p 1660968 --profile cpu-sampling --duration 00:00:30 -o cpu.nettrace
perf top -p 1660968
strace -fp 1660968 -tt -T

现场结论摘要

  • 当前环境是 .NET 6 arm64
  • 直接安装最新版 dotnet-trace 会失败,因为最新版需要 .NET 8
  • 需要明确指定 6.* 版本安装 dotnet-tracedotnet-stackdotnet-counters
  • 排查高 CPU 的首选方法是:
    • 先看线程
    • 再看 counters
    • 再抓 stack
    • 最后用 trace/perf 精确定位

后续建议

如果后续还要继续排查,可以把以下内容补充到这份文档中:

  1. 实际安装成功的工具版本号
  2. dotnet-counters 的实时输出截图或文本
  3. dotnet-stack 的多次采样结果
  4. dotnet-trace 分析出的热点方法
  5. 最终定位到的具体代码位置和修复方案
相关推荐
qq_542515412 小时前
Ubuntu 22.04.4 LTS安装ToDesk最新版打不开,无响应?旧版本4.7.2_277版本分享
linux·ubuntu·todesk
火车叼位2 小时前
替代 Tiny Win10 的 Linux 方案:Debian XFCE 精简桌面搭建
linux·运维
小麦嵌入式2 小时前
FPGA入门(四):时序逻辑计数器原理与 LED 闪烁实现
linux·驱动开发·stm32·嵌入式硬件·fpga开发·硬件工程·dsp开发
皮卡蛋炒饭.3 小时前
传输层协议UDP
linux·网络协议·udp
syagain_zsx3 小时前
Linux指令初识(实用篇)
linux·运维·服务器
王木风4 小时前
终端里的编程副驾:DeepSeek-TUI-项目深度拆解,实测与原理分析
linux·运维·人工智能·rust·node.js
槑槑紫4 小时前
windows系统装轻量版linux开发
linux·运维·服务器
齐潇宇4 小时前
k8s-Helm管理器
linux·运维·云原生·容器·kubernetes
Irene19914 小时前
(课堂笔记)Linux 基础命令:文件增删改、重命名、压缩等
linux
脆皮炸鸡7555 小时前
库制作与原理~动态链接
linux·开发语言·经验分享·笔记·学习方法