.NET 药厂业务系统 CPU爆高分析

Windbg 分析

1. CPU 真的爆高吗

还是老规矩,要想找到这个答案,可以使用 !tp 命令。


0:044> !tp
logStart: 1
logSize: 200
CPU utilization: 88 %
Worker Thread: Total: 8 Running: 4 Idle: 4 MaxLimit: 1023 MinLimit: 4
Work Request in Queue: 0
--------------------------------------
Number of Timers: 2
--------------------------------------
Completion Port Thread:Total: 2 Free: 2 MaxFree: 8 CurrentLimit: 2 MaxLimit: 1000 MinLimit: 4

从卦中数据看当前cpu确实达到了 88%,接下来我们观察下这个程序的机器cpu是否给力,可以用 !cpuid 观察。


0:044> !cpuid
CP  F/M/S  Manufacturer     MHz0  6,94,3  GenuineIntel    31921  6,94,3  GenuineIntel    31922  6,94,3  GenuineIntel    31923  6,94,3  GenuineIntel    3192

从卦中看,尼玛也就4core,有点弱哈,好歹也是一个高利润的药厂,这么抠门哈。

2. 为什么会CPU爆高

导致 CPU 爆高的因素有很多,没有标准答案,需要自己去找原因,首先我们观察下这个程序的线程数量,可以使用 !t 命令即可。


0:044> !t
ThreadCount:      451
UnstartedThread:  0
BackgroundThread: 443
PendingThread:    0
DeadThread:       1
Hosted Runtime:   noLock  DBG   ID     OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception0    1     22b8 04CE8728     26020 Preemptive  18E5C92C:18E5E4DC 04c86c20 -00001 STA 3    2     17c8 04B25768     2b220 Preemptive  18CAF3A0:18CB1374 04c86c20 -00001 MTA (Finalizer) 4    4     238c 04C0CDD8   202b020 Preemptive  18E45D88:18E464DC 04c86c20 -00001 MTA 5    5     230c 0A6C37A0   202b020 Preemptive  18DAC318:18DAC47C 04c86c20 -00001 MTA 6    6     23a0 0A70E620   202b220 Preemptive  00000000:00000000 04c86c20 -00001 MTA ...

从卦中数据看,当前有 451 个线程,其中后台线程是 443 个,再结合刚才的 !tp 看到的线程池线程也才 8 个,这就说明这个程序中有 400+ 的线程是直接通过 new Thread 创建的,这个信息就比较可疑了,为啥不用线程池用 Thread ,有蹊跷。

接下来的思路就是使用 ~*e !clrstack 命令观察下每个线程此时都在做什么,命令一输入,刷了好久。


0:044> ~*e !clrstack
...
OS Thread Id: 0x220c (18)
Child SP       IP Call Site
184CF614 77dd19dc [HelperMethodFrame: 184cf614] System.Threading.Thread.SleepInternal(Int32)
184CF680 141975f4 System.Threading.Thread.Sleep(Int32) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @ 357]
184CF694 165055b9 xxx.ActionThread`1[[xxx]].Loop()
184CF878 74467741 System.Threading.Thread+StartHelper.Callback(System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @ 42]
184CF888 7446fca1 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs @ 183]
184CF8C0 74466742 System.Threading.Thread.StartCallback() [/_/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 105]
184CFA14 74cbc29f [DebuggerU2MCatchHandlerFrame: 184cfa14] 
...

在卦中的各个线程栈上也没有看到什么特别明显的业务函数,大多都是停在 Thread.SleepInternal 上进行等待,这就让我陷入了迷茫。

3. 一朝顿悟,走出迷茫

CPU不可能无缘无故的爆高,总会是那些线程给抬起来的,但这个程序中的线程大多都在 Thread.SleepInternal 上,若说他们能把 CPU 弄爆总有点说不过去。

但问题总得要解决,在无突破口的情况也只能硬着头皮在 Thread.SleepInternal 上强行突破了,首先用 Ctrl+F 搜下有多少线程卡在 SleepInternal 上,截图如下:

尼玛,几乎所有线程都在 Sleep,一般来说有这么多线程都在 Sleep 也是少数,接下来抽一个线程看看业务方法是怎么进行 Sleep 的,参考代码如下:

在这个Loop方法中我发现有很多的 Sleep(1),看到这个我突然想到了高频的上下文切换导致的 CPU 爆高。

接下来这个代码的指令到底停在哪个方法呢?可以反编译 Loop 方法。


0:047> !clrstack
OS Thread Id: 0xad8 (47)
Child SP       IP Call Site
20B5F434 77dd19dc [HelperMethodFrame: 20b5f434] System.Threading.Thread.SleepInternal(Int32)
20B5F4A0 141975f4 System.Threading.Thread.Sleep(Int32) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @ 357]
20B5F4B4 1f123c71 xxx.ActionThread`1[[xxx].Loop()
20B5F698 74467741 System.Threading.Thread+StartHelper.Callback(System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @ 42]
20B5F6A8 1baab7da System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs @ 183]
20B5F6E0 74466742 System.Threading.Thread.StartCallback() [/_/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 105]
20B5F834 74cbc29f [DebuggerU2MCatchHandlerFrame: 20b5f834] 
0:047> !U /d 1f123c71
Normal JIT generated code
xxx.ActionThread`1[xxx].Loop()
ilAddr is 0A324040 pImport is 08AD6468
Begin 1F123C10, size abd
1f123c10 55              push    ebp
1f123c11 8bec            mov     ebp,esp
1f123c13 57              push    edi
1f123c14 56              push    esi
1f123c15 81ecd4010000    sub     esp,1D4h
1f123c1b c5f877          vzeroupper
1f123c1e c5d857e4        vxorps  xmm4,xmm4,xmm4
1f123c22 c5fa7fa524feffff vmovdqu xmmword ptr [ebp-1DCh],xmm4
1f123c2a c5fa7fa534feffff vmovdqu xmmword ptr [ebp-1CCh],xmm4
1f123c32 b850feffff      mov     eax,0FFFFFE50h
1f123c37 c5fa7f6405f4    vmovdqu xmmword ptr [ebp+eax-0Ch],xmm4
1f123c3d c5fa7f640504    vmovdqu xmmword ptr [ebp+eax+4],xmm4
1f123c43 c5fa7f640514    vmovdqu xmmword ptr [ebp+eax+14h],xmm4
1f123c49 83c030          add     eax,30h
...
1f123c5a e84115cc55      call    coreclr!JIT_DbgIsJustMyCode (74de51a0)
1f123c5f 90              nop
1f123c60 90              nop
1f123c61 e9300a0000      jmp     xxx.ActionThread<xxx>.Loop+0xa86 (1f124696)
1f123c66 90              nop
1f123c67 b901000000      mov     ecx,1
1f123c6c e87f54eaea      call    09fc90f0 (System.Threading.Thread.Sleep(Int32), mdToken: 06002D01)
>>> 1f123c71 90              nop
...

通过卦中的 >>> 可以确认很多的方法都是在 while (!base.IsTerminated) 中进行空转,如果 Sleep(1) 的线程比较少那可能没什么问题,但也扛不住400多线程一起玩哈,最后高频的上下文切换导致的 CPU 爆高。

在 Sleep(1) 内部会涉及到CPU的等待队列,就绪队列,以及定时器 _KTIMER 内核对象, 因为 Windows 源码不公开,内部还是比较搞的,可以用 !pcr 命令观察下 cpu的背包。


lkd> !pcr 0
KPCR for Processor 0 at fffff8058023c000:Major 1 Minor 1NtTib.ExceptionList: fffff80589089fb0NtTib.StackBase: fffff80589088000NtTib.StackLimit: 000000137e1fa158NtTib.SubSystemTib: fffff8058023c000NtTib.Version: 000000008023c180NtTib.UserPointer: fffff8058023c870NtTib.SelfTib: 000000137dfe0000SelfPcr: 0000000000000000Prcb: fffff8058023c180Irql: 0000000000000000...CurrentThread: ffff910c66906080NextThread: 0000000000000000IdleThread: fffff80583d27a00DpcQueue: lkd> dt nt!_KPRCB fffff8058023c180+0x008 CurrentThread    : 0xffff910c`66906080 _KTHREAD+0x010 NextThread       : (null) +0x018 IdleThread       : 0xfffff805`83d27a00 _KTHREAD...+0x7c00 WaitListHead     : _LIST_ENTRY [ 0xffff910c`5ec30158 - 0xffff910c`628b1158 ]+0x7c80 DispatcherReadyListHead : [32] _LIST_ENTRY [ 0xfffff805`80243e00 - 0xfffff805`80243e00 ]

上面的[32]就是等待线程的32个优先级的数组队列。

有了上面的分析结果,最后就是告诉朋友做到如下两点:

  • 减少 Thread.Sleep(1) 的线程参与数。
  • 尽量将 1 -> 50 来缓解,当然越大越好。

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

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

相关文章

如何本地搭建Zblog网站并通过内网穿透将个人博客发布到公网

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站…

Navicat里MySQL表的创建(详细)

我以Navicat连接MySQL为例&#xff0c;演示表的创建方法。 前提 创建表的语法&#xff1a; create table 表名 &#xff08; 字段名1&#xff0c;字段类型&#xff0c; 字段名2&#xff0c;字段类型&#xff0c; ...... 字段名n&#xff0c;字段类型 ); 我计划在test库存放一…

第三方登录-pc支付宝扫码登录流程

最近有个奇葩的需求&#xff0c;用户要支持支付宝扫码登录。这个需求很少见&#xff0c;那就做一下&#xff0c;看起来有点难&#xff0c;其实很简单。 先看结果 流程梳理 核心代码 获取支付宝扫码页面的url // 获取支付宝扫码登录页面的urlasync function getZFBLoginUrl()…

动态规划--三步问题

本题题目链接备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/three-steps-problem-lcci/ 个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 动态规划&…

Centos 8.5 Oracle12c安装

由于多次安装踩坑&#xff0c;所以本次写了一份12c安装的完整版。可以直接使用。 一、安装数据库基本信息 名称 值 主机名 database 操作系统 CentOS Linux release 8.5.2111 Oracle用户名/密码 oracle Oracle 版本 12c Enterprise Edition Release 12.2.0.1.0 oracle…

Java 基础学习(十五)集合排序、Lambda和Stream

1 集合排序 1.1 集合排序API 1.1.1 集合排序概述 集合排序是指对一个集合中的元素按照特定规则进行重新排列&#xff0c;以使得集合中的元素按照预定义的顺序呈现。 在集合排序中&#xff0c;通常需要定义一个比较规则&#xff0c;这个比较规则用于决定集合中的元素在排序后…

【Linux C | 文件I/O】文件的打开关闭 | open、creat、colse 函数

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

【LeetCode刷题笔记(11-1)】【Python】【和为 K 的子数组】【前缀和】【中等】

文章目录 引言和为 K 的子数组题目描述提示 解决方案1&#xff1a;【暴力枚举】解决方案2&#xff1a;【前缀和】结束语 和为 K 的子数组 引言 编写通过所有测试案例的代码并不简单&#xff0c;通常需要深思熟虑和理性分析。虽然这些代码能够通过所有的测试案例&#xff0c;但…

SQL进阶理论篇(十七):数据库主从同步的原理

文章目录 简介为什么需要主从同步主从同步的原理总结参考文献 简介 以MySQL数据库为例&#xff0c;在实际生产中&#xff0c;我们会如何对MySQL数据库进行性能优化呢&#xff1f; 比如说配合上Redis做缓存。Redis是一种高性能的内存数据库&#xff0c;而MySQL是一种基于磁盘文…

docker-compose安装Rocketmq总结,以及如何更换mq端口

默认你已经装好了docker哈 安装docker-compose sudo curl -L https://github.com/docker/compose/releases/download/1.25.1-rc1/docker-compose-uname -s-uname -m -o /usr/local/bin/docker-composechmod x /usr/local/bin/docker-composedocker-compose --version成功打印…

截断霍夫曼编码

截断霍夫曼编码是一种数据压缩技术&#xff0c;它基于霍夫曼编码的原理&#xff0c;通过截断霍夫曼树&#xff0c;减少编码中的冗余信息&#xff0c;实现更高效的数据压缩。在本文中&#xff0c;我们将详细探讨截断霍夫曼编码的原理、应用及其优势。 一、霍夫曼编码简介 霍夫曼…

12.21 汇编点亮STM32MP157小灯

.text .global _start _start: 时钟使能pb6 pf6 pe9LDR r0,0x50000A28LDR r1,[r0]ORR r1,r1,#(0x1<<4)ORR r1,r1,#(0x1<<5)ORR r1,r1,#(0x1<<1)STR r1,[r0]配置GPIO模式LDR r0,0x50006000LDR r1,[r0]BIC r1,r1,#(0x2<<20)ORR r1,r1,#(0x1<<20)B…

kubernetes集群应用 service进阶

kubernetes集群应用 Service进阶 一、场景 使用kubernetes集群运行工作负载时&#xff0c;由于Pod经常处于用后即焚状态&#xff0c;Pod对应的IP地址也会经常变化&#xff0c;因此我们不能直接访问Pod&#xff0c;可以通过Service对应的端点列表&#xff08;Endpoints&#x…

PaddleOCR Docker 容器快捷调用,快捷调用OCR API

文章目录 搞环境命令行测试Python调用测试转fastapi服务打包成镜像服务PaddleOCR 服务端模型总结&#xff0c;直接启动OCR服务 paddleOCR迎来大更新&#xff0c;搞一把新的api接口&#xff0c;直接用起来。 搞环境 搞容器&#xff1a; FROM nvidia/cuda:11.8.0-cudnn8-devel…

Python importlib模块详细教程

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com importlib模块是Python标准库中用于动态导入模块的工具。它提供了一系列函数&#xff0c;允许以编程方式加载、检查和操作模块。本文将深入探讨importlib的各种用法&#xff0c;并通过丰富的示例代码帮助你更好地…

华清作业day46

.text .global _start _start: led1 设置时钟使能 ldr r0,0x50000A28 ldr r1,[r0] orr r1,r1,#(0x1<<4) str r1,[r0]设置输出模式 ldr r0,0x50006000 ldr r1,[r0] bic r1,r1,#(0x3<<20) orr r1,r1,#(0x01<<20) str r1,[r0]设置推挽输出 ldr r0,0x5000600…

在QT Creator下用CMake编译GEOS库

最近&#xff0c;想要在C下编一个可用GDAL模块的地图管理系统&#xff0c;找来找去&#xff0c;找到了GEOS。GEOS&#xff08;Geometry Engine-Open Source&#xff09;开源几何引擎 是一个用于计算几何的JTS库的 C/C实现&#xff0c;专注于地理信息系统 &#xff08;GIS&#…

速通Python基础语法--语句篇

缩进和代码块 一 条件语句if/elif if的基本语法 示例 条件语句嵌套 练习1 练习2 二 空语句 三 while循环 示例 打印1-10 求1-100的和 求5! 求1-5的阶乘之和 四 for循环 for的基本语法 1~range[ ,) 五 continue/break continue:结束当前循环,继续下一次循环 break:结束整个循…

《每天一分钟学习C语言·四》文本读写操作及二进制读写

fopen(参数1,参数2)&#xff0c;第一个参数是要打开的文件名&#xff0c;文件名字符串&#xff0c;第二个参数用于制定文件打开模式的一个字符串。 注&#xff1a;如果要打开某个盘的文本如F盘test文件夹下的test.txt文本&#xff0c;则fopen(“F:\test\test.txt”,”r”); 程序…

AcWing算法进阶课-1.1.2Dinic/ISAP求最大流

算法进阶课整理 CSDN个人主页&#xff1a;更好的阅读体验 原题链接 题目描述 给定一个包含 n n n 个点 m m m 条边的有向图&#xff0c;并给定每条边的容量&#xff0c;边的容量非负。 图中可能存在重边和自环。求从点 S S S 到点 T T T 的最大流。 输入格式 第一行包…