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. 最终定位到的具体代码位置和修复方案
相关推荐
.千余2 小时前
【Linux】开发工具1
linux·运维·服务器·c语言·学习
Ops菜鸟(Xu JieHao)2 小时前
Linux Rear系统热备份 【详细教程】
linux·运维·服务器·linux备份·系统备份·rear·热备份
小袁搬码2 小时前
Ubuntu2026.04LTS_长期支持本已发布
linux·ubuntu2026.04
快乐的划水a2 小时前
单片机仿Linux驱动开发(三)
linux·驱动开发·单片机
hhcs2 小时前
Linux TTM 子系统:ttm_mem_reg → ttm_resource
linux·drm mm·drm ttm
сокол2 小时前
【网安-Web渗透测试-Linux提权】CVE-2023-22809
linux·服务器·网络安全
一个人旅程~2 小时前
Q4OS-linuxDebian内核版本下载链接
linux·经验分享·电脑
YQ_013 小时前
大幅提速 colcon build —— ccache 缓存 + 并行数控制防爆内存
linux·缓存·机器人·ros2
被java抛弃的网工3 小时前
Linux基础--挣点元子(1)
linux·运维·服务器