记一次 .NET 某医院HIS系统 CPU爆高分析

一:背景

1. 讲故事

前几天有位朋友加 wx 抱怨他的程序在高峰期总是莫名其妙的cpu爆高,求助如何分析?

和这位朋友沟通下来,据说这问题困扰了他们几年????,还请了微软的工程师过来解决,无疾而终,应该还是没找对微软的大佬。。。

关于程序CPU爆高的问题,老读者应该知道我都写了好几篇了,基本上归为两类:

  • GC 触发

  • 大量 lock 锁

少部分就是各种莫名其妙的问题了,无法一一列举 ????????????,既然朋友找到我了,我得想办法给他解决,话不多聊,上 windbg。

二:windbg 分析

1. 查看同步块表

遇到这种问题,首查 同步块表 已经是我的惯性思维了,命令很简单 !syncblk


0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info  SyncBlock Owner
-----------------------------
Total           20779
CCW             16
RCW             21
ComClassFactory 0
Free            16490

我去,扑了一个空,同步块表中啥也没有。。。既然和锁没关系,那就看看线程吧,毕竟线程就是靠 CPU 养着的。

2. 查看线程

要想查看系统中的 托管线程 ,可以使用 !t 命令, 线程比较多,稍微简化一下。


0:000> !t
ThreadCount:      135
UnstartedThread:  0
BackgroundThread: 132
PendingThread:    0
DeadThread:       1
Hosted Runtime:   noLock  ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception34    1 25d4 000001ea28702130    28220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 0     Ukn 74    2 3ed0 000001ea286fa940    2b220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 0     MTA (Finalizer) 76    3 4a70 000001f4447d7810  102a220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 0     MTA (Threadpool Worker) 77    4 326c 000001f4447dbe60    21220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 0     Ukn 78    6 2dac 000001f4447d9750  1020220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 0     Ukn (Threadpool Worker) 79    7 1468 000001f444a2d6f0    21220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 1     Ukn (GC) 80    8   f0 000001f444a2cf20    21220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 0     Ukn 81    9 3118 000001f444a2f630    21220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 0     Ukn 

先卖个关子,可有朋友看出这些线程有什么异样???对,线程 79 的最后一列有一个 Ukn (GC) 标记,我想你肯定好奇,这说明什么?由于底层GC的模式有些变更,但不管怎么说,它在一定程度上告诉你,你的程序触发了GC,为了进一步验证,可以用 !t -special 看下 79 号线程到底属于什么类别以及更加详细的信息。


0:000> !t -special
ThreadCount:      135
UnstartedThread:  0
BackgroundThread: 132
PendingThread:    0
DeadThread:       1
Hosted Runtime:   noLock  ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception34    1 25d4 000001ea28702130    28220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 0     Ukn 79    7 1468 000001f444a2d6f0    21220 Preemptive  0000000000000000:0000000000000000 000001ea286ee080 1     Ukn (GC) OSID Special thread type
41 38d8 DbgHelper 
42 1c88 GC 
74 3ed0 Finalizer 
75 402c ProfilingAPIAttach 
76 4a70 Timer
79 1468 GC SuspendEE 

从最后一行输出中可以看到, 79就是GC线程,后面还有一个奇怪的 SuspendEE 标记,你又好奇了,这又啥意思?

SuspendEE =  Suspend CLR Execution Engine (EE)

也就是说,79号线程把 CLR执行引擎 给冻结了,目的很简单,就是为了方便其他31个GC线程打理当前的 托管堆,不过这老哥机器真????????,32core,也不知道哪家医院这么给力,补充一下,用 !cpuid 验证。


0:000> !cpuid
CP  F/M/S  Manufacturer     MHz0  6,62,4  <unavailable>   26001  6,62,4  <unavailable>   26002  6,62,4  <unavailable>   26003  6,62,4  <unavailable>   2600xxx
31  6,62,4  <unavailable>   2600

既然预判到了是 GC 触发,下一步就可以把所有线程的托管和非托管堆栈全部调出来。

3. 查看各个线程栈

要想查看各个线程的托管和非托管栈很简单, 使用 !eestack 即可,然后可以检索关键词 WaitUntilGCComplete 来判断有多少线程在等待GC处理完毕。

从图中可以看出,当前有 40 个线程被阻塞了,真好,问题越来越清晰了,接下来再分析到底是哪个线程做了什么不该做的事,导致 GC 触发,同样也可以搜 try_allocate_more_space 来判断哪些线程正在分配空间。

我去,可以很明显的看出当前 170187 号线程正在分配大对象 gc_heap::allocate_large_object 触发了 GC,本身大对象堆是一个令人生畏的东西,对它的回收清扫都是非常耗CPU资源的,这也和朋友说到的 1分钟左右CPU就下降了 的情况比较吻合。

4. 寻找祸首

现在关注点就在这两个线程上了,我看了下这两个线程栈都是同一处方法,所以这里就挑一个 187 线程来分析吧,可以用 !clrstack 看下它的托管栈。


0:187> !clrstack 
OS Thread Id: 0x1ef0 (187)Child SP               IP Call Site
00000054ce631e30 00007ffc4021bde2 System.String.FillStringChecked(System.String, Int32, System.String)
00000054ce631e70 00007ffc402222a8 System.String.ConcatArray(System.String[], Int32)
00000054ce631ed0 00007ffc40223528 System.String.Concat(System.String[])
00000054ce631f40 00007ffbe6dbdafb BLL.xxx.xxx.GetRowString(System.String, Boolean, Boolean, System.String, System.String, System.String, System.String, System.String, System.String, Int32, System.String, System.String, System.String, System.String, System.String, System.String, Int32, Int32, System.String, System.Nullable`1, System.Nullable`1, System.String, System.Nullable`1, System.Nullable`1, System.Nullable`1, System.Nullable`1, System.String, System.String, System.String, System.String, System.String ByRef)
00000054ce65cf40 00007ffbe6ab3187 BLL.xxx.xxx.ExpZB(System.String, Boolean, Boolean, System.String, System.String, System.String, System.String, Int32, System.String, System.String, System.String, Int32, System.String, System.String, System.String, System.String, System.String, System.String, Int32, Int32, System.String, System.Nullable`1, System.Nullable`1, System.String, System.Nullable`1, System.Nullable`1, System.Nullable`1, System.Nullable`1, System.String, System.String, System.String, System.String)

从堆栈上看,貌似是使用了 System.String.Concat 拼接 string 所致,好家伙,string 拼接这么多年不知道被人抨击了多少次,还是有很多的人踩坑????????????,为了进一步验证,可以使用 !clrstack -p + !da -details xxx 看看这个 System.String[] 到底是什么,简化如下:


0:187> !clrstack -p
OS Thread Id: 0x1ef0 (187)
00000054ce631e70 00007ffc402222a8 System.String.ConcatArray(System.String[], Int32)PARAMETERS:values (<CLR reg>) = 0x000001ea69e8d2f8totalLength = <no data>
0:187> !da -details 0x000001ea69e8d2f8
Name:        System.String[]
Size:        128(0x80) bytes
Array:       Rank 1, Number of elements 13, Type CLASS
Element Methodtable: 00007ffc403d6948
[0] 000001f2391a83f0Name:        System.StringMethodTable: 00007ffc403d6948EEClass:     00007ffc3fcd50e0Size:        445950(0x6cdfe) bytesFile:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dllString:      xxxxx

从输出信息中可以看到,String[] 里面有 13 个元素,其中最大的一个 string 是 445950 bytes = 445k,大于大对象的85k界定,所以这里的 Concat 就是症结所在,同样 170 线程也是如此,接下来我还得要解决的一个问题是:为什么会有如此大的字符串产生,代码里面到底做了什么???要想寻找答案,还得从dump中导出源码一探究竟。

5. 查看问题代码

要想分析问题代码,可以通过 !ip2md + !savemodule 导出 BLL.xxx.xxx.GetRowString 方法。


0:187> !ip2md 00007ffbe6dbdafb
MethodDesc:   00007ffbe5342118
Method Name:  BLL.xxx.xxx.GetRowString(System.String, Boolean, Boolean, System.String, System.String, System.String, System.String, System.String, System.String, Int32, System.String, System.String, System.String, System.String, System.String, System.String, Int32, Int32, System.String, System.Nullable`1<Int32>, System.Nullable`1<Int32>, System.String, System.Nullable`1<Single>, System.Nullable`1<Single>, System.Nullable`1<Single>, System.Nullable`1<Single>, System.String, System.String, System.String, System.String, System.String ByRef)
Class:        00007ffbe52fe328
MethodTable:  00007ffbe53421d8
mdToken:      0000000006000096
Module:       00007ffbe471a890
0:187> !savemodule  00007ffbe471a890 E:\dumps\RowString.dll
3 ps in file
p 0 - VA=2000, VASize=f7fcc, FileAddr=200, FileSize=f8000
p 1 - VA=fa000, VASize=3bc, FileAddr=f8200, FileSize=400
p 2 - VA=fc000, VASize=c, FileAddr=f8600, FileSize=200

然后祭出 ILSpy 反编译这段代码。

好家伙,这写法真????????,无数的字符串拼接,gen2LOH堆 都来不及分配内存段了????????????,真的是害死 GC 了。。。

三:总结

其实这是一个教科书式的问题,也有教科书式的解决方法,而且我看了下这个方法有 600 多行的代码,基本上都是做string拼接的事,最后说一下解决方案。

  • 重构该方法,尽量用 StringBuilder 替代 String ,将因这种 GC 触发的次数降到最低。

最后的小彩蛋,貌似这个分析结果和朋友的深度怀疑不约而同。。。

END

工作中的你,是否已遇到 ... 

1. CPU爆高

2. 内存暴涨

3. 资源泄漏

4. 崩溃死锁

5. 程序呆滞

等紧急事件,全公司都指望着你能解决...  危难时刻才能展现你的技术价值,作为专注于.NET高级调试的技术博主,欢迎微信搜索: 一线码农聊技术,免费协助你分析Dump文件,希望我能将你的踩坑经验分享给更多的人。

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

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

相关文章

mysql判断表存在的sql语句_SQL 语句判断已知表是否存在_MySQL

问:怎样用SQL语句来判断已知表是否存在?答:具体解决方法如下:注释:以下代码为通常的引用Dao做的一模块以下为引用的内容&#xff1a;Function fExistTable(strTableName As String) As IntegerDim db As DatabaseDim i As IntegerSet db DBEngine.Workspaces(0).Databases(0)…

全球六大顶级域名动态:7月上旬.COM新增18.4万个

中国IDC评述网07月17日报道&#xff1a;据域名统计机构Whois Source公布的最新数据显示&#xff0c;截至7月15日&#xff0c;全球六大顶级域名&#xff08;.COM、.NET、.ORG、.INFO和.US&#xff09;的域名总量达到了 141,259,651个。其中&#xff0c;.COM域名注册量达104,165,…

如何用大数据找到男/女朋友?

全世界只有3.14 % 的人关注了数据与算法之美小柯25岁&#xff0c;单身男&#xff0c;热衷大数据&#xff0c;并决定认真钻研&#xff0c;用数据分析来实现自己的“脱单计划”。找女友第一步&#xff1a;整理思路找女友第二步&#xff1a;界定问题1、为什么要找女朋友&#xff0…

.NET5 开发手机提词应用,基于内嵌Web服务器及PowerPoint自动化

项目说明我使用电脑录制视频教程的时候&#xff0c;会展示PPT给观众&#xff0c;同时也有一些提示性的文字给我自己看。这就类似于很多电视节目录制现场的“提词器”。节目录制现场的提词器在PC环境下&#xff0c;PowerPoint也具有提词器功能&#xff0c;在编辑PPT的时候&#…

[Linux程序设计][调试][ElectricFence]

gcc –o test test.c –lefence 提前发现动态内存的错误 转载于:https://blog.51cto.com/honglei/934379

薄如冈本,37°恒温发热超薄保暖内衣,既要风度也要温度

问君能有几多愁恰似没穿秋裤遇寒流俗话说的好你在北方的暖气里穿着短袖我在南方的艳阳里瑟瑟发抖为了暖和一点大家都穿得里三层外三层的什么大衣棉袄厚外套都搬出来了但谁都不愿意在冬天穿的像个200斤的胖子好吗&#xff01;&#xff1f;可是为了风度连狗命都不要了吗&#xff…

Blazor Day

关注我们Blazor 是一个 Web UI 框架&#xff0c;Blazor 旨在简化快速的单页面 .Net 浏览器应用的构建过程&#xff0c;它虽然使用了诸如 CSS 和 HTML 之类的 Web 技术&#xff0c;但它使用 C&#xff03;语言和 Razor 语法代替 JavaScript 来构建可组合的 Web UI 。通过提供用于…

学会了Python之后,我的职业生涯突飞猛进

全世界只有3.14 % 的人关注了数据与算法之美身为职场人&#xff0c;收集上万条表格数据做商业分析&#xff0c;裁剪上千张图片&#xff0c;发送数百封邮件...这些都是经常会遇到的场景。我一直期待能有个工具解放我&#xff0c;直到我遇到了Python。Python的魅力很多小伙伴入坑…

Android网络编程

2019独角兽企业重金招聘Python工程师标准>>> Android平台有三种网络接口可以使用&#xff0c;他们分别是&#xff1a;java.net.*(标准Java接口)、Org.apache接口和Android.net.*(Android网络接口)。下面分别介绍这些接口的功能和作用。 1.标准Java接口 java.net.*提…

mac 启动mysql多实例_实践:mysql单机多实例部署(mac)

背景&#xff1a;在自己电脑搭建或测试分布式服务框架时&#xff0c;经常会用多个数据库实例模拟多个环境的情况&#xff0c;因此我把搭建多实例mysql的过程记录下来&#xff0c;方便互相学习和沟通。1.搭建环境1) mac 电脑&#xff0c;版本 10.15.62) mysql版本 8.0.202.搭建…

毫无疑问的是.NET 在信创常用软件适配清单之中

2020年8月份写了一篇文章《.NET Core也是国产化信息系统开发的重要选项》&#xff0c; 这又过去了大半年了&#xff0c;在信创领域发生了很大的变化&#xff0c;今天写这篇文章主要是想从信创常用软件适配清单 看一看.NET 在信创里面的情况。信创常用软件适配清单 是由中国电子…

学习Python编程的19个资源

全世界只有3.14 % 的人关注了数据与算法之美用Python编写代码一点都不难&#xff0c;事实上它一直被赞誉为最容易学的编程语言。如果你准备学习web开发&#xff0c; Python是一个不错的开始&#xff0c;甚至想做游戏的话&#xff0c;用Python来开发游戏的资源也有很多。这是快速…

读《精益商业思维》

五一假期读了程浩的《精益商业思维》&#xff0c;程浩是迅雷的联合创始人之一&#xff0c;现在是职业投资人&#xff0c; 全篇从创业者的角度&#xff0c;也从投资人的角度解析了创业的方法论。书中有大量的互联网公司的案例&#xff0c;都是我们耳熟能详的一些互联网企业&…

mysql中两根竖线什么意思_五线谱中两根竖线是什么意思?

五线谱中的竖线叫做终止线&#xff0c;写在乐曲结束处的右边一条略粗的双纵线。音乐总是由强拍和弱拍交替进行的&#xff0c;这种交替不能杂乱无章、任意安排&#xff0c;而是按照一定的规律构成最小的节拍组织一小节&#xff0c;然后以此为基础循环往复。比如&#xff0c;当两…

三张图读懂机器学习 :基本概念、五大流派与九种常见算法

全世界只有3.14 % 的人关注了数据与算法之美机器学习正在进步&#xff0c;我们似乎正在不断接近我们心中的人工智能目标。语音识别、图像检测、机器翻译、风格迁移等技术已经在我们的实际生活中开始得到了应用&#xff0c;但机器学习的发展仍还在继续&#xff0c;甚至被认为有可…

go 切片取最后一个元素_深挖 Go 之 forrange 排坑指南

今年做个 Dig101 系列&#xff0c;挖一挖技术背后的故事。Dig101: dig more, simplified more and know moregolang 常用的遍历方式&#xff0c;有两种&#xff1a;for 和 for-range。而 for-range 使用中有些坑常会遇到&#xff0c;今天我们一起来捋一捋。文章目录0x01 遍历取…

SqlServer SqlParser 介绍及基本使用

SqlServer SqlParser 介绍及使用示例Intro最近发现在 Nuget 上有一个 SqlServer 的 SqlParser&#xff0c;利用 SqlParser 我们做到可以解析 SQL 的每一部分 &#xff0c;nuget 包是公开的&#xff0c;可以拿来即用&#xff0c;只是缺少使用示例&#xff0c;很多功能需要自己去…

如何使用TensorFlow玩转深度学习?

自 2015 年 11 月 9 号发布之后&#xff0c;TensorFlow 逐渐成为人工智能领域最广泛运用的深度学习框架。那么TensorFlow框架到底是什么&#xff1f;TensorFlow 是一个大规模机器学习的开源框架&#xff0c;提供了多种深度神经网络的支持。不仅 Google 在自己的产品线使用 Tens…

大学,我是怎么边学编程边赚钱的?

我是如何在大学时就靠编程赚钱的&#xff1f;大家好&#xff0c;我是鱼皮&#xff0c;前段时间看到一位朋友的问题&#xff1a;穷极客一枚&#xff0c;正值大学&#xff0c;很想自己解决生活问题&#xff0c;不再向父母要钱。计算机相关专业&#xff0c;喜欢编程&#xff0c;觉…

独占设备的分配与回收_灵魂拷问:Java对象的内存分配过程是如何保证线程安全的?...

点击上方“linkoffer”&#xff0c;选择关注公众号高薪职位第一时间送达作者 l HollisJVM内存结构&#xff0c;是很重要的知识&#xff0c;相信每一个静心准备过面试的程序员都可以清楚的把堆、栈、方法区等介绍的比较清楚。上图&#xff0c;是一张在作者根据《Java虚拟机规范(…