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

2023-12-22 00:45

本文主要是介绍.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 来缓解,当然越大越好。

这篇关于.NET 药厂业务系统 CPU爆高分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/522078

相关文章

Node.js net模块的使用示例

《Node.jsnet模块的使用示例》本文主要介绍了Node.jsnet模块的使用示例,net模块支持TCP通信,处理TCP连接和数据传输,具有一定的参考价值,感兴趣的可以了解一下... 目录简介引入 net 模块核心概念TCP (传输控制协议)Socket服务器TCP 服务器创建基本服务器服务器配置选项服

CSS3 最强二维布局系统之Grid 网格布局

《CSS3最强二维布局系统之Grid网格布局》CS3的Grid网格布局是目前最强的二维布局系统,可以同时对列和行进行处理,将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局,本文介... 深入学习 css3 目前最强大的布局系统 Grid 网格布局Grid 网格布局的基本认识Grid 网

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创

CentOS系统Maven安装教程分享

《CentOS系统Maven安装教程分享》本文介绍了如何在CentOS系统中安装Maven,并提供了一个简单的实际应用案例,安装Maven需要先安装Java和设置环境变量,Maven可以自动管理项目的... 目录准备工作下载并安装Maven常见问题及解决方法实际应用案例总结Maven是一个流行的项目管理工具

MySQL的cpu使用率100%的问题排查流程

《MySQL的cpu使用率100%的问题排查流程》线上mysql服务器经常性出现cpu使用率100%的告警,因此本文整理一下排查该问题的常规流程,文中通过代码示例讲解的非常详细,对大家的学习或工作有一... 目录1. 确认CPU占用来源2. 实时分析mysql活动3. 分析慢查询与执行计划4. 检查索引与表

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

C#实现系统信息监控与获取功能

《C#实现系统信息监控与获取功能》在C#开发的众多应用场景中,获取系统信息以及监控用户操作有着广泛的用途,比如在系统性能优化工具中,需要实时读取CPU、GPU资源信息,本文将详细介绍如何使用C#来实现... 目录前言一、C# 监控键盘1. 原理与实现思路2. 代码实现二、读取 CPU、GPU 资源信息1.

在C#中获取端口号与系统信息的高效实践

《在C#中获取端口号与系统信息的高效实践》在现代软件开发中,尤其是系统管理、运维、监控和性能优化等场景中,了解计算机硬件和网络的状态至关重要,C#作为一种广泛应用的编程语言,提供了丰富的API来帮助开... 目录引言1. 获取端口号信息1.1 获取活动的 TCP 和 UDP 连接说明:应用场景:2. 获取硬