如何在.NET应用程序中分析CPU使用率过高的问题

原文来自互联网,由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权,请联系小编,小编将在24小时内删除。限于译者的能力有限,个别语句翻译略显生硬,还请见谅。

作者:胡安·帕勃罗·希达,JUAN PABLO SCIDA是一位软件架构师,在软件开发方面拥有10多年的经验。他是经过认证的.NET和Java开发人员。在过去的几年中,他还热衷于使用Node.js,MongoDB和Erlang。

原文来自:https://www.toptal.com/dot-net/hunting-high-cpu-usage-in-dot-net

软件开发可能是一个非常复杂的过程。作为开发人员,我们需要考虑很多不同的变量。有些不在我们的控制之下,有些在实际代码执行时对我们来说是未知的,有些则由我们直接控制。 .NET开发人员[1]也毫不例外。

考虑到这样的现实情况,当我们在受控环境中工作时,事情通常会按计划进行。假设就是我们的开发机器或我们可以完全访问的集成环境。我们可以使用工具来分析影响我们的代码和软件的不同变量。我们也不必处理服务器的繁重负载,也不必处理并发用户尝试同时执行相同操作的情况。

在可描述和安全的情况下,我们的代码通常可以正常工作,但是在生产环境下,如果处于过度负载或其他一些外部因素的影响,可能会发生意外问题。生产环境的软件性能很难分析。在大多数情况下,我们必须在理论上处理潜在的问题:我们知道可能会发生问题,但无法测试。这就是为什么我们需要以我们所用语言的最佳实践和文档为基础进行开发,并避免常见错误[2]

如前所述,当软件上线时,可能会出错,并且代码可能会以我们未计划的方式开始执行。当我们不得不处理问题而又无法调试或确定发生了什么情况时,下我们该怎么办?

图片

如果某个进程长时间使用超过90%的CPU,则我们会遇到麻烦

在本文中,我们将分析基于Windows的服务器上. net web应用程序的高CPU使用率的实际案例场景、涉及到的识别问题的过程,以及更重要的问题,为什么会出现这个问题以及我们如何解决它。

CPU使用率和内存消耗是广泛讨论的主题。通常,很难确定某个特定进程应使用的资源(CPU,RAM,I / O)的正确数量以及持续的时间段。尽管可以肯定的是-如果某个进程长时间使用了超过90%的CPU,那么我们将特别麻烦,因为在这种情况下服务器将无法处理任何其他请求。

这是否意味着流程本身存在问题?不必要。该过程可能需要更多的处理能力,或者正在处理大量数据。首先,我们唯一能做的就是尝试确定发生这种情况的原因。

所有操作系统都有几种不同的工具来监视服务器中发生的事情。Windows服务器专门具有任务管理器Performance Monitor[3],在本例中,我们使用了New Relic Servers[4],它是监视服务器的绝佳工具。

最初症状和问题分析

部署应用程序后,在头两周的时间里,我们开始看到服务器的CPU使用率达到峰值,这使服务器无响应。为了使其再次可用,我们必须重新启动它,并且该事件在该时间段内发生了3次。如前所述,我们使用New Relic Servers作为服务器监视器,它表明w3wp.exe在服务器崩溃时,该进程占用了94%的CPU。

Internet信息服务(IIS)工作进程是Windows进程(w3wp.exe),它运行Web应用程序,并负责处理发送到特定应用程序池的Web服务器的请求。IIS服务器可能有多个应用程序池(和几个不同的w3wp.exe进程),这些池可能会产生问题。根据该进程具有的用户(这在New Relic报告中显示),我们确定问题出在我们的.NET C#Web表单旧版应用程序。

.NET Framework与Windows调试工具紧密集成在一起,因此,我们要做的第一件事是查看事件查看器和应用程序日志文件,以查找有关正在发生的事情的有用信息。无论我们是否在事件查看器中记录了一些异常,它们都没有提供足够的数据来进行分析。这就是为什么我们决定更进一步并收集更多数据的原因,因此当事件再次发生时,我们将做好准备。

数据采集

收集用户模式进程转储的最简单方法是使用Debug Diagnostic Tools v2.0[5]或仅使用DebugDiag。DebugDiag具有一组用于收集数据(DebugDiag集合)和分析数据(DebugDiag分析)的工具。

因此,让我们开始定义使用调试诊断工具收集数据的规则:

1.打开DebugDiag集合,然后选择Performance。

图片

2.选择Performance Counters并单击Next。3.点击Add Perf Triggers。4.展开Processor(不是Process)对象,然后选择% Processor Time。请注意,如果您使用的是Windows Server 2008 R2,并且具有64个以上的处理器,请选择该Processor Information对象而不是该Processor对象。5.在实例列表中,选择_Total。6.单击Add,然后单击确定OK。7.选择新添加的触发器,然后单击确定Edit Thresholds。

图片

8.Above在下拉菜单中选择。

9.将阈值更改为80。

10.输入20秒数。

您可以根据需要调整该值,但请注意不要指定小数秒,以防止错误触发。

图片

11.点击OK。

12.点击Next。

13.点击Add Dump Target。

14.Web Application Pool从下拉菜单中选择。

15.从应用程序池列表中选择您的应用程序池。

16.点击OK。

17.点击Next。

18.Next再点击一次。

19.如果需要,请输入规则名称,并记下转储的保存位置。

您可以根据需要更改此位置。

20.点击Next。

21.选择Activate the Rule Now并单击Finish。

描述的规则将创建一组小型转储文件,这些文件的大小将非常小。最终转储将是具有完整内存的转储,并且该转储会更大。现在,我们只需要等待高CPU事件再次发生即可。

将转储文件保存在所选文件夹中后,我们将使用DebugDiag Analysis工具来分析收集的数据:

1.选择性能分析器。

图片

2.添加转储文件。

图片

3.开始分析。

DebugDiag将花费几分钟(或数分钟)来解析转储并提供分析。完成分析后,您将看到一个网页,其中包含摘要以及有关线程的大量信息,类似于以下内容:

图片

正如您在摘要中看到的那样,有一条警告说:“在一个或多个线程上检测到转储文件之间的CPU使用率过高。” 如果单击建议,我们将开始了解应用程序存在问题的地方。我们的示例报告如下所示:

图片

正如我们在报告中看到的那样,有一个关于CPU使用率的模式。所有CPU使用率高的线程都与同一类相关。在跳到代码之前,让我们看一下第一个。

图片

这是我们遇到的第一个线程的细节。对我们来说有趣的部分是:

图片

在这里,我们有一个代码调用,GameHub.OnDisconnected()该代码触发了有问题的操作,但是在此调用之前,我们有两个Dictionary调用,它们可以使您对发生的事情有所了解。让我们看一下.NET代码,看看该方法在做什么:

public override Task OnDisconnected() {

try{var userId = GetUserId();string connId;if(onlineSessions.TryGetValue(userId, out connId))onlineSessions.Remove(userId);}catch(Exception){// ignored}returnbase.OnDisconnected();}

我们显然在这里有问题。报告的调用堆栈说问题出在字典上,在这段代码中我们正在访问字典,特别是引起问题的那一行是:

if (onlineSessions.TryGetValue(userId, out connId))

这是字典声明:

static Dictionary<int, string> onlineSessions = new Dictionary<int, string>();

.NET代码有什么问题?

具有面向对象编程经验的每个人都知道静态变量将由此类的所有实例共享。让我们更深入地了解.NET世界中静态的含义。

根据.NET C#规范:

使用static[6]修饰符声明一个静态成员,该成员属于类型本身而不是特定对象。

这就是.NET C#语言规范关于静态类和成员的说明[7]

与所有类类型一样,当加载引用该类的程序时,.NET Framework公共语言运行库(CLR)将加载静态类的类型信息。程序无法确切指定何时加载类。但是,可以保证在程序中首次引用该类之前,将其加载并初始化其字段并调用其静态构造函数。静态构造函数仅被调用一次,并且静态类在程序所在的应用程序域的生存期内保留在内存中。非静态类可以包含静态方法,字段,属性或事件。即使没有创建该类的实例,该静态成员也可以在该类上调用。始终通过类名称而不是实例名称访问静态成员。无论创建多少个类实例,静态成员只有一个副本。静态方法和属性无法访问其包含类型的非静态字段和事件,并且除非在方法参数中显式传递了实例变量,否则它们无法访问任何对象的实例变量。

这意味着静态成员属于类型本身,而不是对象。它们也由CLR加载到应用程序域中,因此静态成员属于承载应用程序的进程,而不是特定线程。

鉴于Web环境是多线程环境,因为每个请求都是由w3wp.exe进程产生的新线程;考虑到静态成员是该过程的一部分,我们可能会遇到以下情况:几个不同的线程尝试访问静态(由多个线程共享的)变量的数据,这最终可能会导致多线程问题。

线程安全性下的Dictionary 文档[8]声明以下内容:

Dictionary<TKey, TValue>只要不修改集合,A 就可以同时支持多个阅读器。即使这样,通过集合进行枚举本质上也不是线程安全的过程。在极少的枚举与写访问竞争的情况下,必须在整个枚举期间锁定集合。要允许多个线程访问该集合进行读写,您必须实现自己的同步。

此声明解释了为什么我们可能会遇到此问题。根据转储信息,问题出在字典的FindEntry方法上:

图片

如果查看字典的FindEntry 实现,[9]我们可以看到该方法遍历内部结构(存储桶)以查找值。

因此,以下.NET代码枚举了集合,这不是线程安全的操作。

publicoverrideTaskOnDisconnected() {
try
{
var userId = GetUserId();
string connId;
if(onlineSessions.TryGetValue(userId, out connId))onlineSessions.Remove(userId);
}
catch(Exception)
{
// ignored
}
returnbase.OnDisconnected();
}

结论

正如我们在转储中看到的那样,有多个线程试图同时迭代和修改共享资源(静态字典),最终导致迭代进入无限循环,从而导致线程消耗超过90%的CPU。。

有几种可能的解决方案。我们首先实现的方法是锁定和同步对字典的访问,但会损失性能。那时服务器每天都崩溃,因此我们需要尽快解决此问题。即使这不是最佳解决方案,它也解决了该问题。

解决这个问题的下一步是分析代码并找到最优解决方案。重构代码是一个选项:新的ConcurrentDictionary类可以解决这个问题,因为它只锁定在一个桶级别,这将提高整体性能。尽管这是一大步,还需要进一步的分析。

References

[1] .NET开发人员: https://www.toptal.com/dot-net
[2] 常见错误: https://www.toptal.com/c-sharp/top-10-mistakes-that-c-sharp-programmers-make
[3] Performance Monitor: https://technet.microsoft.com/en-us/library/cc749115.aspx
[4] New Relic Servers: http://newrelic.com/server-monitoring
[5] Debug Diagnostic Tools v2.0: https://www.microsoft.com/en-us/download/details.aspx?id=49924
[6] static: https://msdn.microsoft.com/en-us/library/98f28cdx.aspx
[7] 静态类和成员的说明: https://msdn.microsoft.com/en-us/library/79b3xss3.aspx
[8] 文档: https://msdn.microsoft.com/en-us/library/xfhwa508%28v=vs.100%29.aspx
[9] 实现,: http://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,bcd13bb775d408f1

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

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

相关文章

HashMap实现原理

HashMap HashMap基础数据结构&#xff1a; 如上结构课看出&#xff0c;HashMap主要是有一个链表的形式来存储数据 &#xff0c;上面Node类和C语言中的结构体很像&#xff0c;如上可以看出HashMap底层由是一个数组结构&#xff0c;数组中的每一项又是一个链表&#xff0c;新建一…

做好技术管理,你必须要跨越的4道槛

点击蓝字关注&#xff0c;回复“职场进阶”获取职场进阶精品资料一份读者群里有不少刚开始做管理的技术人&#xff0c;很多都和我谈过他们的困惑。总结下来主要是不知道继续晋升需要培养哪方面的能力。技术经理其实是技术人最难做好的管理岗&#xff0c;原因主要有两方面&#…

图的最小生成树和最短路径算法思路总结(Prim,Kruskal,Dijkstra,Floyd)

带权无向图—>最小生成树算法—>Prim算法: 思路: 首先&#xff0c;我们先设置两个集合&#xff0c;U_{}&#xff1a;一个用来放最小生成树的顶点&#xff0c;T_{}&#xff1a;一个用来放最小生成树的边。选取最开始的点V_0&#xff0c;将V_0放入U_{}中&#xff0c;得到U_…

玩转控件:对Dev的GridControl控件扩展

缘由一切实现来源于需求&#xff0c;目的在于不盲目造轮子&#xff0c;有小伙伴儿在看了《玩转控件:对Dev中GridControl控件的封装和扩展》文章后&#xff0c;私信作者说&#xff0c;因公司业务逻辑比较复杂&#xff0c;展示字段比较多&#xff0c;尤其网格列表控件展示数据太多…

二叉排序树(搜索树BST)-详解结点的删除

在二叉排序树中删除一个结点时&#xff0c;需保证删除后的二叉树仍然是二叉排序树。为讨论方便&#xff0c;假定被删除结点为p&#xff0c;其双亲结点为f。删除的过程可按下述的两种情况分别处理。 在这里我们用红色三角形表示我们要删除的结点&#xff0c;蓝色表示我们要改变指…

java调优方法,jvm监控工具

graph LR A-->B性能概述 程序性能表现形式 执行速度&#xff1a;程序响应速度&#xff0c;总耗时是否足够短内存分配&#xff1a;内存分配是否合理&#xff0c;是否过多消耗内存或者存在泄漏启动时间&#xff1a;程序运行到可以正常处理业务需要的时间负载承受能力 性能测…

那位五十多岁的创业者给我的启示!

作者&#xff1a;邹溪源&#xff0c;长沙资深互联网从业者&#xff0c;架构师社区特邀嘉宾&#xff01;一我曾经提到过最终改行从事美缝行业的老w&#xff0c;他靠自己的“不够努力”&#xff0c;最终离开了行业。但是&#xff0c;这个世界其实有点讽刺。在沉迷于安逸小日子的老…

平衡二叉树(AVL树)-详解平衡调整

平衡调整: (注意&#xff1a;平衡调整只是平衡调整&#xff0c;没有进行结点的插入) LL型调整: (带阴影的小框表示插入的结点) 代码如下: AVLNode *AVLTree::LL_Rotate(AVLNode *a) {AVLNode *b;b a->lchild;a->lchild b->rchild;b->rchild a;a->bf b-&g…

初识消息队列/RabbitMQ详解

欢迎大家阅读《朝夕Net社区技术专刊》我们致力于.NetCore的推广和落地&#xff0c;为更好的帮助大家学习&#xff0c;方便分享干货&#xff0c;特创此刊&#xff01;很高兴你能成为忠实读者&#xff0c;文末福利不要错过哦&#xff01;今天来给大家分享关于消息队列的内容&…

zookeeper理解

Zookeeper简介 Zookeeper的数据模型 层次化的目录结构&#xff0c;命名符合常规文件系统规范每个节点在zookeeper中叫做znode&#xff0c;并且有一个唯一的路径标识节点znode可以包含数据和子节点&#xff0c;但是EPHEMERAL类型的节点不能有子节点Znode中的互刷可以有多个版本…

那些年,在MSRA实习过的女孩,现在都怎么样了?

编者按&#xff1a;我们用两周时间回访了五位在 MSRA 实习过的女孩&#xff0c;她们也都是当年“实习派”的主人公。我们本想在更长的时间维度下&#xff0c;看一看 MSRA 给予她们的改变。然而超乎预期的是&#xff0c;在她们身上&#xff0c;坚持的力量比改变更强。变与不变&a…

Zookeeper理解---ZAB协议

ZAB协议 Zookeeper并不是完全采用Paxos算法&#xff0c;而是使用了一种称为Zookeeper Atomic Broadcast&#xff08;ZAB&#xff0c;Zookeeper原子消息广播协议&#xff09;作为数据一致性的核心算法&#xff0c;依据此算法来实现分布式数据一致性的解决。他是一种特别为Zooke…

【最强VSCode】之管理MySql数据库

(梅花香自苦寒来)你没有看错&#xff0c;就是用VSCode来管理MySql数据库&#xff0c;我也是第一次听说&#xff0c;感谢群管理DX小伙伴&#xff0c;三人行必有我师焉。话不多说&#xff0c;直接开张&#xff0c;VSCode还是很不错的&#xff0c;以后多多分享插件吧。1、你平时是…

[Flags]标识的Enum不能使用Html.GetEnumSelectList方法

在使用Asp.Net Core MVC写程序时&#xff0c;对用户类型做了如下定义&#xff1a;namespace ManufacturingExecutionSystemCore.Enums {public enum UserType{[Description("无身份人员")][Display(Name "无身份人员")]UnidentifiedPerson0x00,[Descripti…

Zookeeper实践与应用- Canal

基于MySql BinLog的增量订阅和消费组件&#xff1a;Canal Cancal是阿里13年1月开源的一个基于MySql数据库Binlog实现的增量订阅和消费的组件。项目取名Canal取自管道的英文单词&#xff0c;流转的医生&#xff0c;是一个定位于基于MySql数据库Binlog增量日志来实现数据库镜像&…

你遇到的面试官是「伯乐」吗?

之前写了一篇应应聘者视角的「面试中要注意的点」&#xff0c;《聊聊面试的事&#xff08;应聘方&#xff09;》。这次再来一篇面试官视角的。如果你不是面试官&#xff0c;也没关系。所谓“知己知彼&#xff0c;方能百战百胜”&#xff0c;了解一下面试官在面试时的侧重点&…

记录一次线上超时异常查询

线上事故复盘 前言 前一次上线&#xff0c;当时正常&#xff0c;第二天发现有部分超时报警&#xff0c;最终发现应为Dubbo接口一次传输数据量太大导致线程虚拟内存占用 线上问题排查过程 报警邮件中查询到有一部分接口超时量激增&#xff0c;查询定位到某个Dubbo接口&#x…

没用过.gitignore还敢自称高级开发?

Git是跟踪项目中所有文件的好工具&#xff0c; 但是&#xff0c;您会希望在项目的整个生命周期中不要跟踪某些文件及其变更。系统文件&#xff08;i.e. Mac系统的.Ds_Store&#xff09;应用程序配置文件&#xff08;i.e. app.config, .env&#xff09;构建组件&#xff08;i.e.…

支付价格计算中精度问题之double,float

前言 前段时间开发新的微信小程序&#xff0c;借此机会将老掉牙的支付模块重构&#xff0c;并且支持现金支付&#xff08;之前都是虚拟币支付&#xff09;&#xff0c;在重构期间遇到计算上的一些精度问题&#xff0c;虽然数额影响非常小但是影响比较大&#xff0c;我觉得有必…

.net core 集成 sentry 进行异常报警

.net core 集成 sentry 进行异常报警IntroSentry 是一个实时事件日志记录和汇集的平台。其专注于错误监控以及提取一切事后处理所需信息而不依赖于麻烦的用户反馈。它分为客户端和服务端&#xff0c;客户端(目前客户端有 C#, Python, PHP, JavaScript, Ruby等多种语言)就嵌入在…