某工控图片上传服务 CPU 爆高分析

news2025/7/7 20:08:13

一:背景

1.讲故事

今天给大家带来一个入门级的 CPU 爆高案例,前段时间有位朋友找到我,说他的程序间歇性的 CPU 爆高,不知道是啥情况,让我帮忙看下,既然找到我,那就用 WinDbg 看一下。

二:WinDbg 分析

1. CPU 真的爆高吗

其实我一直都在强调,要相信数据,口说无凭,一定要亲自验证一下,可以使用 !tp 命令。


0:000> !tp
CPU utilization: 81%
Worker Thread: Total: 32 Running: 0 Idle: 18 MaxLimit: 2047 MinLimit: 2
Work Request in Queue: 0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 0 Free: 0 MaxFree: 4 CurrentLimit: 0 MaxLimit: 1000 MinLimit: 2

从卦中可以看到,当前的 CPU=81%,果然爆高无疑,接下来就得调查下为什么会爆高,可以从触发 GC 入手。

2. GC 触发了吗

要观察是否 GC 触发,可以观察下线程列表上是否有 (GC) 字样,比如下面的输出。


0:006> !t
ThreadCount:      38
UnstartedThread:  0
BackgroundThread: 37
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                         Lock  
       ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
   0    1  5f0 01310688     2a020 Preemptive  00000000:00000000 0130aa50 0     MTA 
   2    2  818 0131e358     2b220 Preemptive  00000000:00000000 0130aa50 0     MTA (Finalizer) 
   3    6  7b0 01374908   202b220 Preemptive  00000000:00000000 0130aa50 0     MTA 
   4    7  f98 01381c50   102a220 Preemptive  00000000:00000000 0130aa50 0     MTA (Threadpool Worker) 
   6    3  610 013eba78     2b220 Cooperative 00000000:00000000 0130aa50 1     MTA (GC) 
   9   44  e04 05585068   1029220 Preemptive  00000000:00000000 0130aa50 0     MTA (Threadpool Worker) 
  10   25  448 063dab30     21220 Preemptive  00000000:00000000 0130aa50 0     Ukn 
  ...

从卦中可以看到6号线程果然带了 (GC) 字样,接下来用 kb 观察下到底是哪一代GC。


0:006> kb
 # ChildEBP RetAddr      Args to Child              
00 05beef18 72bb4825     0b771000 00000003 00000001 clr!WKS::gc_heap::relocate_survivor_helper+0x87
01 05beef48 72bb46da     0b771000 00000001 00000000 clr!WKS::gc_heap::relocate_survivors+0x93
02 05beef98 72bb1913     00000000 00000001 73180140 clr!WKS::gc_heap::relocate_phase+0x8b
03 05bef140 72bb0f69     00000000 00000001 00000001 clr!WKS::gc_heap::plan_phase+0x13b8
04 05bef168 72bb12ef     5e7aa9c3 7317fcd0 00000000 clr!WKS::gc_heap::gc1+0xe8
05 05bef1a0 72bb140c     00000040 7317ff04 7317ff04 clr!WKS::gc_heap::garbage_collect+0x447
06 05bef1c8 72bb161c     00000000 00000000 00000040 clr!WKS::GCHeap::GarbageCollectGeneration+0x1fb
07 05bef1ec 72bb1696     7317ff04 71a9d900 00000002 clr!WKS::gc_heap::trigger_gc_for_alloc+0x1e
08 05bef21c 72bff51a     00000000 00000040 0c1c7aa4 clr!WKS::gc_heap::try_allocate_more_space+0x162
09 05bef230 72bff687     00000000 01304d38 72bff140 clr!WKS::gc_heap::allocate_more_space+0x18
0a 05bef24c 72ab4477     013ebab8 00000040 00000002 clr!WKS::GCHeap::Alloc+0x5c
0b 05bef26c 72ab44f5     01000000 71ab5e90 05bef3f8 clr!Alloc+0x87
0c 05bef2b4 72ab4595     5e7aab5f 00000bb8 05bef3f8 clr!AllocateObject+0x99
0d 05bef33c 719b8281     71a2417c 05bef358 05bef35c clr!JIT_New+0x6b
0e 05bef360 7225652d     00000000 00000000 00000000 mscorlib_ni!System.Threading.Tasks.Task.Delay+0x41 [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 5885] 
0f 05bef454 05a9d18a     00000000 00000000 00000000 mscorlib_ni!System.Threading.Tasks.Task.Delay+0xd [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 5843] 
...

因为 C++ 默认是 this 协定,从 clr!WKS::gc_heap::plan_phase+0x13b8 方法的第二个参数 00000001 可知,当前触发了 1 代 GC,其实 1 代 GC 本来就触发频繁,所以问题不大,主要就是看是否为 2 代GC,即 FullGC。

到这里,GC触发的路堵死了,我们就看下是不是还有其他的可疑情况,比如高时钟个数的线程。

3. 有长时间运行线程吗

如果是当事人,可以用 Process Explorer 工具直接观察 Thread 列表的 Cycles Delta 列就能知道,比如下面的百度云管家,

可以看到 11156 号线程占用了太多的时钟周期个数,可惜我不是当事人,所以只能用 cpuid 命令观察。


0:006> !runaway
 User Mode Time
  Thread       Time
    6:610      0 days 0:47:07.984
   10:448      0 days 0:11:32.531
   12:17d4     0 days 0:01:34.265
    9:e04      0 days 0:01:29.468
   11:16ec     0 days 0:01:11.562
   13:1458     0 days 0:01:07.703
   ...

从卦中可以看到,6号线程耗费的时钟个数遥遥领先,甩了第二名 10 号线程几条街,这个线程非常可疑,得好好研究下它的托管栈了。


0:006> !clrstack
OS Thread Id: 0x610 (6)
Child SP       IP Call Site
05bef2d0 72bb47ae [HelperMethodFrame: 05bef2d0] 
05bef344 719b8281 System.Threading.Tasks.Task.Delay(Int32, System.Threading.CancellationToken) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 5885]
05bef36c 7225652d System.Threading.Tasks.Task.Delay(Int32) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 5843]
05bef370 05a9d18a xxx.Api.Core.xxx+c__DisplayClass2_0.<.cctor>b__0()
05bef45c 719b7118 System.Threading.Tasks.Task.InnerInvoke() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2884]
05bef468 719b6cc0 System.Threading.Tasks.Task.Execute() [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2498]
05bef48c 719b70ea System.Threading.Tasks.Task.ExecutionContextCallback(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2861]
05bef490 719d40c5 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 954]
05bef4fc 719d3fd6 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 902]
05bef510 719b6f68 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2827]
05bef574 719b6e72 System.Threading.Tasks.Task.ExecuteEntry(Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs @ 2756]
05bef584 71a2acbc System.Threading.Tasks.ThreadPoolTaskScheduler.LongRunningThreadWork(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\Tasks\ThreadPoolTaskScheduler.cs @ 49]
05bef588 719a70e3 System.Threading.ThreadHelper.ThreadStart_Context(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\thread.cs @ 74]
05bef594 719d40c5 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 954]
05bef600 719d3fd6 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 902]
05bef614 719d3f91 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 891]
05bef62c 71a28cae System.Threading.ThreadHelper.ThreadStart(System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\thread.cs @ 93]
05bef770 72a90096 [GCFrame: 05bef770] 
05bef954 72a90096 [DebuggerU2MCatchHandlerFrame: 05bef954] 

从线程栈上看还好有一个托管方法 xxx.Api.Core.xxx+c__DisplayClass2_0.<.cctor>b__0 ,接下来观察下源码,修剪后的代码如下:


static xxxUploadPool()
{
    _queue = new ConcurrentQueue<xxxModel>();
    _xxx = new xxxService();
    int second = Configuration.xxx * 1000;
    Task.Factory.StartNew(delegate
    {
        while (true)
        {
            lock (_queue)
            {
                if (_queue.Count > 0 && _queue.TryDequeue(out var result))
                {
                    _xxx.UploadFilexxxx(result._path, result._repositoryName, xxx);
                }
            }
            Task.Delay(second);
        }
    }, TaskCreationOptions.LongRunning);
}

这段代码很有意思,它的本来想法就是开启一个长线程,然后在长线程中不断的轮询等待,问题就出在了这个等待上, 即 Task.Delay(second); 这句, 这句代码起不到任何作用,而且一旦 _queue 中的数据为空就成了死循环, 给 CPU 打满埋下了祸根。

这里有一个疑问:一个线程能把 CPU 打满,那太瞧不起CPU 了,肯定是有对等的 core 个数的线程一起发力,打爆CPU,那如何验证? 观察下 CPU 的个数。


0:006> !cpuid
CP  F/M/S  Manufacturer     MHz
 0  6,85,4  GenuineIntel    3193
 1  6,85,4  GenuineIntel    3193

也就说只要有两个线程进入了 xxxUploadPool 那就够了,现象也正是如此。

三:总结

这段代码确实很有意思,猜测原来就是 Thread.Sleep(second) ,但为了赶潮流改成了 Task.Delay(second),在不清楚后者的使用场景下给 CPU 间歇性爆高埋下了祸根,所以大家在使用新的语法时,一定要弄清楚场景,万不可生搬硬套。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/7002.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux进程概念和控制(必备知识)

文章目录1、冯诺依曼体系结构2、操作系统3、进程<1>进程的创建<2>进程查看<3>进程状态<4>进程优先级<5> 进程地址空间4、环境变量5、进程控制<1>进程终止<2>进程等待<3>进程替换1、冯诺依曼体系结构 我们常见的计算机&#x…

软考 - 软件工程

软件过程基本概述 基本要素 方法工具过程 软件过程模型 能力成熟度模型CMM 能力成熟度模型CMMI 统一过程UP模型 针对大型项目 三大特别 用例和风险驱动以架构为中心迭代并且增量 四个阶段 起始&#xff1a;确认需求和风险评估精化&#xff1a;核心架构设计构建&#xff1a;构…

Linux内核开发 | Linux内核目录结构分析(5.4.32)

文章目录1. arch2. block3. certs4. crypto5. Documentation6. drivers7. fs8. include9. init10. ipc11. kernel12. lib13. mm14. net15. samples16. scripts17. security18. sound19. tools20. usr21. virt本文以Linux主线5.4.32内核版本进行分析。1. arch 该目录下包含了li…

【ROS】机械人开发--ROS工作空间与功能包

机械人开发--ROS工作空间与功能包一、ROS工作空间1.1 概念1.2 创建工作空间1.3 编译工作空间1.4 设置环境变量1.5 添加环境变量二、功能包2.1 概念2.2 功能包的内容2.3 创建功能包三、CMakeLists.txt文件四、package.xml文件一、ROS工作空间 1.1 概念 工作空间&#xff08;wo…

以“新IT”助“数智融合”,联想推开“智能化转型”下半场的大门

作者 | 曾响铃 文 | 响铃说 近年来&#xff0c;我国对数字化的重视达到前所未有的高度&#xff0c;从“十四五”规划纲要首次将数字经济单独列为一篇&#xff1b;到二十大报告中指出&#xff1a;“坚持把发展经济的着力点放在实体经济上”、“促进数字经济和实体经济深度融合…

SpringMVC学习篇(五)

SpringMVC之json数据传递 1.1 准备工作 1.1.1 导入lombok依赖(方便写实体类) <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency>1.1.2 导入mvc js…

mysql经典案例带解析(你没见过的全新版本)55题

首先给出初始表格 表格创建命令 create table emp(id int primary key auto_increment,name varchar(20),job varchar(20),manager int,hiredate date,sal double(8,2),comm double(6,2),dept_id int)charsetutf8;create table dept(id int primary key auto_increment,nam…

AcrelEMS-BP生物制药工厂能效管理系统

安科瑞 华楠 聚焦全厂能源采集、监控、分析、调度,降本提效,实现企业双碳目标;致力于全域化设备监视、巡检、故障报警、工单管理,运维优化,提升设备使用效率。 综合自动化系统 110kV及以下变电站综合自动化系统实现遥测、遥信、遥控、事故追忆、故障录波、安全防护、上传调度 …

搭建接口平台YApi详解(含搭建node环境)

公司之前使用的doclever 感觉不太好用&#xff0c;打算私有化部署YApi 步骤 准备使用yapi的可视化部署&#xff0c;需要有node环境 安装node环境 测试一下有没有node环境 如下就是有 [root192 sbin]# node -v v14.17.0 [root192 sbin]# npm -v 6.14.13没有就创建 cd /us…

webpack5 打包环境抽离分环境基本配置

两种开发模式 开发模式&#xff1a;代码能编译自动化运行生产模式&#xff1a;代码编译优化输出Webpack 基本功能 开发模式&#xff1a;可以编译 ES Module 语法生产模式&#xff1a;可以编译 ES Module 语法&#xff0c;压缩 js 代码Webpack 配置文件5 个核心概念 entryoutput…

FL Studio21最新演示测试版本下载FL水果V21

FL Studio是市场上最受欢迎的音乐制作软件之一。它被世界各地的许多专业制作人和艺术家使用。FL Studio音乐软件的每日下载量超过40&#xff0c;000次&#xff0c;其增长是不断的&#xff0c;而且没有迹象表明很快就会放缓。随着新的 FL 产品版本在 Windows 和 Mac 上不断发布&…

25.gateway的Filter 过滤器工厂(springcloud)

1 概述 gateway 里面的过滤器和 Servlet 里面的过滤器&#xff0c;功能差不多&#xff0c;路由过滤器可以用于修改进入Http 请求和返回 Http 响应2 分类 2.1 按生命周期分两种 pre 在业务逻辑之前 post 在业务逻辑之后2.2 按种类分也是两种 GatewayFilter 需要配置某个路由&…

阿里同步神器Canal原理+安装+快速使用

文章目录前言Canal简介MySQL主备复制原理canal 工作原理1、MySQL配置1.1 修改MySQL配置支持binloglog-binmysql-binbinlog-formatROW1.2 创建canal用户1.3 重启mysql服务1.4 基本的查看binlog命令2、下载安装canal2.1 解压canal2.2 配置与mysql信息2.3 启动canal3. 快速使用3.1…

【文件传输】实现下载

文章目录下载&#xff1a;下载的过程&#xff1a;单文件传输&#xff1a;多文件传输&#xff1a;下载&#xff1a; 如下图&#xff1a; 如果刚发完size&#xff0c;客户端不回复ok&#xff0c;直接发送数据&#xff0c;会出现粘包问题。如果想要确保客户端收到数据&#xff0…

流行的前端开源报表工具有哪些?适合在企业级应用的

前端开源报表工具有很多&#xff0c;但是如果是企业级应用真心不建议选用。比如非要选择开源的报表工具&#xff0c;你需要投入一个或多个程序员来做这个事情&#xff0c;他们还得先学习这个开源报表工具的界面、功能使用操作等等&#xff0c;尤其是很多开源工具的学习资料还是…

dpdk Vhost 库

1、 怎么实现vhost_dev的VhostOps的vhost_set_vring_kick和vhost_set_vring_call&#xff1b; vhost_net kernel方式的vhost_set_vring_kick和vhost_set_vring_call依赖于/dev/vhost_net的ioctl。 有两种实现方式&#xff1a; 1、guest是server&#xff0c;dpdk vhost user是…

Sourcetree的实际使用开发笔记

目录 前言 一、Sourcetree是什么 二、Sourcetree使用步骤 1.创建仓库 2.拉取和推送的使用 3.创建新的自己的分支 三、使用Sourcetree来进行不提交本地的代码&#xff0c;而获取最新的代码拉取 总结 前言 本章主要是记录一下Sourcetree的基本使用功能。 一、Sourcetree是…

Android:Binder思考笔记

基础知识 进程空间划分 一个进程空间分为用户空间与内核空间。用户空间与内核空间都是虚拟内存&#xff0c;映射到物理内存。所有进程的内核空间映射到同一块物理内存&#xff0c;是共享的二者区别&#xff1a; 进程间&#xff0c;用户空间的数据不可共享&#xff0c;即用户空…

线性与树型数据结构可视化模拟器

线性与树型数据结构可视化模拟器 题目2:线性与树型数据结构可视化模拟器 [问题描述] 数据结构课程是计算机类专业的核心课程之一&#xff0c;是计算机科学与技术必修的专业基础课程。数据结构研究的范围和计算机软件有着密切的联系。课程涉及到大量的概念、定义以及数据结构的…

JS数据类型的探究

JS数据类型的探究 分思考三部曲? 是什么?为什么?怎么做? 一:什么是数据类型? 在程序设计的类型系统中&#xff0c;数据类型&#xff08;英语&#xff1a;Data type&#xff09;&#xff0c;又称资料型态、资料型别&#xff0c;是用来约束数据的解释。在编程语言中&…