记一次 .NET 某资讯论坛 CPU爆高分析

大概有11天没发文了,真的不是因为懒,本想前几天抽空写,不知道为啥最近求助的朋友比较多,一天都能拿到2-3个求助dump,晚上回来就是一顿分析,有点意思的是大多朋友自己都分析了几遍或者公司多年的牛皮藓问题,真的是心太累,不过也好,累那是走上坡路😂😂😂。

再回到正题,在一个月前,有位朋友wx找到我,他最近也在学习如何分析dump,可能经验不是很丰富,分析不下去了,截图如下:

c590f36082b27a463514d65332f7be0b.png

虽然dump中的问题千奇百怪,但如果要汇成大类,还是有一些规律可循的,比如:gc频繁触发,大量锁 等等,详细汇总可以观摩我的星球,好了,既然分析不下去,那就上 windbg。

二:Windbg 分析

1. 查看CPU利用率

既然报过来说cpu过高,我得用数据验证下不是,老命令 !tp

0:057> !tp
CPU utilization: 100%
Worker Thread: Total: 51 Running: 30 Idle: 0 MaxLimit: 400 MinLimit: 4
Work Request in Queue: 11Unknown Function: 6a0bbb30  Context: 1b4ca258Unknown Function: 6a0bbb30  Context: 1b4ca618Unknown Function: 6a0bbb30  Context: 1b4ca758Unknown Function: 6a0bbb30  Context: 1cb88d60Unknown Function: 6a0bbb30  Context: 1b4ca798Unknown Function: 6a0bbb30  Context: 1b5a54d0AsyncTimerCallbackCompletion TimerInfo@01f6e530Unknown Function: 6a0bbb30  Context: 1b5a5a50Unknown Function: 6a0bbb30  Context: 1cb892a0Unknown Function: 6a0bbb30  Context: 1b4ca8d8Unknown Function: 6a0bbb30  Context: 1cb88da0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 1 Free: 1 MaxFree: 8 CurrentLimit: 1 MaxLimit: 400 MinLimit: 4

我去,cpu打满了,对了,这里稍微提醒下, CPU utilization: 100% 指的是当前机器而不是程序,言外之意就是当机器的CPU 100% 时,并不一定是你所dump的程序造成的。

2. 是否为 GC 触发

面对这陌生的dump,先进行一些经验性排查,比如说是否为 GC 触发导致? 那怎么去验证这个假设呢?为了让结果更准确一点,用 !t -special 导出线程列表,看看是否有 GC SuspendEE 字样。

0:057> !t -special
ThreadCount:      109
UnstartedThread:  0
BackgroundThread: 74
PendingThread:    0
DeadThread:       35
Hosted Runtime:   noOSID Special thread type14 2594 DbgHelper 15 2be4 GC SuspendEE 16  dc4 GC 17 2404 GC 18  bb4 GC 19 2498 Finalizer 20 312c ProfilingAPIAttach 21  858 Timer 22 3a78 ADUnloadHelper 27 290c GC 28 2e24 GC 29 28b0 GC 30 1e64 GC 38 3b24 ThreadpoolWorker ...90 2948 Gate

从输出看,尼玛果然有,那就表明确实是GC触发所致,如果你还不相信的话,可以参考下 coreclr 源码。

size_t
GCHeap::GarbageCollectGeneration(unsigned int gen, gc_reason reason)
{dprintf (2, ("triggered a GC!"));gc_heap::gc_started = TRUE;{init_sync_log_stats();#ifndef MULTIPLE_HEAPScooperative_mode = gc_heap::enable_preemptive ();dprintf (2, ("Suspending EE"));BEGIN_TIMING(suspend_ee_during_log);GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);END_TIMING(suspend_ee_during_log);gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc();gc_heap::disable_preemptive (cooperative_mode);if (gc_heap::proceed_with_gc_p)pGenGCHeap->settings.init_mechanisms();elsegc_heap::update_collection_counts_for_no_gc();#endif //!MULTIPLE_HEAPS}
}

看到上面的 SuspendEE 的吗,它的全称就是 Suspend CLR Execute Engine,接下来我们用 ~*e !dumpstack 看看哪一个线程触发了 CLR 中的 GarbageCollectGeneration 方法。

9dfcab3fdc16301a72429fce5cafd2b7.png

从图中可以看到是 53 号线程触发了,切到53号线程后换用 !clrstack

9d6dd5179b13bce914e136ed002dd78e.png

从线程栈看,程序做了一个 XXX.GetAll() 操作,一看这名字就蛮恐怖的,接下来我们再看看这块源码,到底做了什么操作,简化后的源码如下:

public static List<xxxx> GetAll(){string text = "xxxProperty_GetAll";SqlDatabase val = new SqlDatabase(m_strConnectionString);xxxPropertyTreeInfo xxxPropertyTreeInfo = null;List<xxxPropertieInfo> list = new List<xxxPropertieInfo>();DbCommand storedProcCommand = ((Database)val).GetStoredProcCommand(text);using (IDataReader reader = ((Database)val).ExecuteReader(storedProcCommand)){while (DataBase.DataReaderMoveNext(reader)){xxxPropertyTreeInfo = new xxxPropertyTreeInfo();xxxPropertyTreeInfo.LoadDataReader(reader);list.Add(xxxPropertyTreeInfo);}}return list;}public virtual void LoadDataReader(MethodBase method, object obj, IDataReader reader){Hashtable hashtable = new Hashtable();for (int i = 0; i < reader.FieldCount; i++){hashtable.Add(reader.GetName(i).ToLower(), reader.GetValue(i));}Hashtable fieldProperties = GetFieldProperties(method, FieldType.DBField);foreach (object key in fieldProperties.Keys){PropertyInfo p = (PropertyInfo)fieldProperties[key];object v = null;if (hashtable.Contains(key)){v = hashtable[key];}if (v != null){SetPropertieValue(ref obj, ref p, ref v);}}}

从源码逻辑看:它执行了一个存储过程 xxxProperty_GetAll , 然后把获取到数据的 reader 和 xxxPropertyTreeInfo 做了一个 mapping 映射,在映射的过程中触发了GC。

3. 是否为数据过大导致?

按照以往经验,应该是从数据库中获取了过多数据导致,那本次dump是不是呢?要想寻找答案, 先用 !dso 命令导出线程栈所有变量,然后用 !do xxx 查看 List<xxxPropertieInfo> list 的size,如下图所示:

9e62fff7429670d534513bc7f85b0461.png

从图中看,这个size并不大,那为什么会导致gc频繁触发呢?就算做了 反射 产生了很多的小对象,应该也没多大影响哈。。。这又让我陷入了沉思。。。

4. 寻找问题根源

经过一顿查找,我发现了几个疑点。

  1. 有24个线程正在执行 XXX.GetALL() 方法。

0758d360cfdb4dcbbd5aa3d25eda7079.png
  1. 托管堆中发现了 123 个 list,大的size 也有 1298,所以合计起来也不小哈。。。

0:053> !dumpheap -mt 1b9eadd0Address       MT     Size
02572a9c 1b9eadd0       24     
026eca58 1b9eadd0       24     
0273d2a0 1b9eadd0       24 
...Statistics:MT    Count    TotalSize Class Name
1b9eadd0      123         2952 System.Collections.Generic.List`1[[xxxPropertieInfo, xxx.Model]]0:053> !DumpObj /d 28261894
Name:        System.Collections.Generic.List`1[[xxxPropertieInfo, xxx.Model]]
MethodTable: 1b9eadd0
EEClass:     6e2c6f8c
Size:        24(0x18) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:MT    Field   Offset                 Type VT     Attr    Value Name
6e6ff32c  4001891        4     System.__Canon[]  0 instance 23710638 _items
6e6f1bc0  4001892        c         System.Int32  1 instance     1298 _size
6e6f1bc0  4001893       10         System.Int32  1 instance     1298 _version
6e6f0100  4001894        8        System.Object  0 instance 00000000 _syncRoot
6e6ff32c  4001895        4     System.__Canon[]  0   static  <no information>
  1. 程序是 32bit

从内存地址就能判断当前程序是 32bit,这就意味着它的 segment 段会很小,也就意味着更多的GC回收。

三:总结

本次事故是由于:

  1. 多个线程频繁重复的调用 size=1298 的 GetALL() 方法。

  2. 使用低效的 反射方式 进行model映射,映射过程中产生了不少的小对象。

  3. 过小的 segment (32M)

三者结合造成GC频繁的触发。

改进方法也很简单。

  • 最简单粗暴的方法:将数据库的查询结果缓存一份。

  • 稍微正规一点方法:用 Dapper 替换低效的 手工反射,将程序改成 64bit 。

和朋友沟通了解,采用了第一种方法,终于把 CPU 摁下去了,一切都恢复了平静!

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

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

相关文章

内存调试技巧

内存调试技巧 2007 年 6 月 21 日 本文将带您了解一些良好的和内存相关的编码实践&#xff0c;以将内存错误保持在控制范围内。内存错误是 C 和 C 编程的祸根&#xff1a;它们很普遍&#xff0c;认识其严重性已有二十多年&#xff0c;但始终没有彻底解决&#xff0c;它们可能严…

数学课本上的几大变态之处

全世界只有3.14 % 的人关注了爆炸吧知识数学课本上的几大变态--完--

使用Redis set 解决数据的唯一性问题

前言最近遇到一个问题&#xff0c;就是接收第三方数据的时候&#xff0c;类似这种直播数据&#xff0c;由于业务的缘故&#xff0c;导致对方给的数据每次都是全量的&#xff0c;而且请求很频繁&#xff0c;有时候一秒好几十次。直播数据一般都是刷刷刷的&#xff0c;这个大家或…

mysql集群从节点无法启动_一次galera cluster集群故障节点无法启动问题排查

现象环境&#xff1a;Server version: 10.0.25-MariaDB-wsrep MariaDB Server, wsrep_25.13.raf7f02e配置文件&#xff1a;[rootnode-23 mariadb]# more /etc/my.cnf[mysqld]server_id3bind_address node-23port 3306datadir/var/lib/mysqllog-error/var/log/mariadb/mariadb…

webform 页面传值的方法总结

ASP.NET页面之间传递值的几种方式 页面传值是学习asp.net初期都会面临的一个问题&#xff0c;总的来说有页面传值、存储对象传值、ajax、类、model、表单等。但是一般来说&#xff0c;常用的较简单有QueryString&#xff0c;Session&#xff0c;Cookies&#xff0c;Application…

iNeuOS工业互联网操作系统,智慧用电测控应用案例

目 录1. 概述... 22. 系统部署结构... 23. 用电测控终端... 34. 系统应用介绍... 61. 概述通过物联网技术对引发电气火灾的主要因素(导线温度、电流和漏电流等)进行不间断的数据跟踪与统计分析&#xff0c;实时发现电气线路和用电设备存在的安全隐…

出现了!豆瓣最高9.9分,2020年最值得看的美剧!你居然还没看过?【内附资源】...

全世界只有3.14 % 的人关注了爆炸吧知识在调性普遍黄暴烧脑的美剧大流中&#xff0c;《This is us》没有大牌主演&#xff0c;没有炫酷特效&#xff0c;却让观众集体沦陷&#xff0c;被称为5年难得一见的美剧。有人说&#xff0c;这是「有生之年看过的最温柔的美剧」。但它取得…

C# 修改配置文件进行窗体logo切换

01—前言&#xff1a;题外的话大家可能发现这个号现在原创越来越少了&#xff0c;其实小编并没有放弃持续更新&#xff0c;只是把一手原创放到了 【dotnet编程大全】这个号了&#xff0c;那个号目前原创主要更新的是wpf mvvm方面的知识&#xff0c;框架用的Caliburn.Micro&…

数学有趣地超乎你的想象

全世界只有3.14 % 的人关注了爆炸吧知识说起数学你是拒绝还是喜欢看完这一组&#xff0c;对于数学他的震撼、霸气、美来感受下哇1三角形内角和为1802多边形外角和为360&#xff08;图来源于可乐学习&#xff09;3怎样将一个正三角形剪拼成正方形&#xff1f;4怎样把两正方形剪拼…

使用C#像google/zx一样编写脚本

google/zxzx是谷歌开源的一个能够帮助开发者快速编写脚本的工具&#xff0c;它使用JavaScript作为编程语言。示例脚本如下&#xff1a;#!/usr/bin/env zxawait $cat package.json | grep namelet branch await $git branch --show-current await $dep deploy --branch${branch…

redis 查询缓存_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....

在实际的工作项目中&#xff0c; 缓存成为高并发、高性能架构的关键组件 &#xff0c;那么Redis为什么可以作为缓存使用呢&#xff1f;首先可以作为缓存的两个主要特征&#xff1a;在分层系统中处于内存/CPU具有访问性能良好&#xff0c;缓存数据饱和&#xff0c;有良好的数据淘…

5部适合学英语的动画电影,快和孩子一起看!

全世界只有3.14 % 的人关注了爆炸吧知识今天我们与大家分享5部非常适合小学生学习英语的动画电影&#xff0c;家长们可依据不同类别和主题为孩子挑选喜欢的影片&#xff0c;在家陪孩子一起观看。文末可免费领取哦~01 《丁丁历险记》讲述的是一天丁丁买了一只古老的船模送给船长…

OC面向对象—封装

OC面向对象—封装 一、面向对象和封装 面向对象的三大特性&#xff1a;封装&#xff08;成员变量&#xff09;、继承和多态 在OC语言中&#xff0c;使用interface和implementation来处理类。 interface就好像暴露在外面的时钟表面&#xff0c;像外界提供展示以及接口。implemen…

10张让你大脑崩溃的图,敢接受挑战吗?

全世界只有3.14 % 的人关注了爆炸吧知识快睡了吧&#xff1f;来做一组视觉游戏~一些人热爱挑战各种错觉&#xff0c;如果你也是这类型图片的粉丝&#xff0c;这10张图片会让你非常过瘾&#xff01;入门篇【挑战一】在这张图片中&#xff0c;你能看到几个红球&#xff1f;5个&am…

Source Generator 单元测试

Source Generator 单元测试IntroSource Generator 是 .NET 5.0 以后引入的一个在编译期间动态生成代码的一个机制&#xff0c;介绍可以参考 C# 强大的新特性 Source GeneratorGetStarted使用起来还算比较简单的&#xff0c;我平时一般用 xunit&#xff0c;所以下面的示例也是使…

又一个中国男人荣获巨奖!拿奖拿的手软,却坦言“我对诺奖没有兴趣”...

全世界只有3.14 % 的人关注了爆炸吧知识获得诺奖似乎只是时间问题2020年9月10日&#xff0c;2021年科学突破奖&#xff08; BREAKTHROUGH PRIZES&#xff09;正式公布。来自中国香港的科学家卢煜明获得了生命科学科学突破奖&#xff0c;华人数学家孙崧获得了数学新视野奖。前几…

ffbe攻略站_最终幻想勇气启示录ffbe兵员强化攻略

最终幻想勇气启示录兵员如何强化&#xff1f;兵员强化后有哪些加强&#xff1f;来看看9k9k小编带来的最终幻想勇气启示录ffbe兵员强化攻略。在兵员选栏中&#xff0c;我们可以看到有强化兵员这一选项&#xff0c;在这里面&#xff0c;我们可以选择兵员进行强化&#xff0c;强化…

不止命令行!自定义VS生成事件

前言在VS中打开项目属性&#xff0c;选择“生成事件”选项卡。在“生成前事件命令行”或“生成后事件命令行”文本框中可以输入任何命令提示符或.bat文件中有效的命令&#xff1a;但是&#xff0c;有没有可能执行更丰富的命令呢&#xff1f;生成事件的本质上面设置的“生成事件…

如果你女朋友不让你看她卸妆......

1 如果你女朋友不让你看她卸妆▼2 扫地机器人的正确用法&#xff08;图源网络&#xff0c;侵删&#xff09;▼3 来比个心&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼4 精彩攻防战▼5 那些吃辣条的小学生长大了...▼6 人生的道路上有时候也要回头看看▼7 先礼…

[9月29日的脚本] 枚举SharePoint列表(PowerShell)

脚本下载: SPListEnumerator.zip http://gallery.technet.microsoft.com/scriptcenter/SPListEnumerator-PowerShell-b0ce0b9f 本脚本通过一个“大型”列表或者是文档库来枚举并为相关项提供信息。 在SharePoint&#xff08;2007版和2010版&#xff09;中&#xff0c;我们有一个…