记一次 .NET 车联网云端服务 CPU爆高分析

一:背景

1. 讲故事

前几天有位朋友wx求助,它的程序CPU经常飙满,没找到原因,希望帮忙看一下。

这些天连续接到几个cpu爆高的dump,都看烦了????????????,希望后面再来几个其他方面的dump,从沟通上看,这位朋友表现的好惨,可能实际更惨,那既然找到我了,我就尽最大能力帮他找到幕后真凶,话不多说,上 windbg。

二:windbg 分析

1. 查看托管线程

因为线程都是靠cpu养着,所以从线程上入手也是一个很好的思路,要想查看程序的所有托管线程,可以使用 !t 命令。


0:000> !t
ThreadCount:      38
UnstartedThread:  0
BackgroundThread: 34
PendingThread:    0
DeadThread:       3
Hosted Runtime:   noLock  ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception0    1 105c 000000000067f600    2a020 Preemptive  0000000000000000:0000000000000000 0000000000671ec0 0     MTA 2    2 13d0 00000000192c4f40    2b220 Preemptive  0000000000000000:0000000000000000 0000000000671ec0 0     MTA (Finalizer) 
...
XXXX   15    0 000000001bc64970  8039820 Preemptive  0000000000000000:0000000000000000 0000000000671ec0 0     MTA (Threadpool Completion Port) 24   23 1380 000000001bc660e0  8029220 Preemptive  0000000000000000:0000000000000000 0000000000671ec0 0     MTA (Threadpool Completion Port) 
XXXX   53    0 000000001bc63200  8039820 Preemptive  0000000000000000:0000000000000000 0000000000671ec0 0     MTA (Threadpool Completion Port) 
XXXX   27 10dc 000000001bd0dbf0  1029220 Preemptive  0000000002CB40F8:0000000002CB4108 0000000000671ec0 1     MTA (GC) (Threadpool Worker) 

在卦象上看:程序有38个线程,死了3个,我去,有一个亮点,最后一行出现了一个熟悉的 MTA (GC) 字样,这什么意思呢?这表示当前线程触发了GC,但奇怪的是,这个触发GC的线程死了,你肯定要问怎么看出来的,看行头的 XXXX,先不管了,死马当活马医,调出线程的所有托管和非托管栈,看看有没有 WaitUntilGCCompletetry_allocate_more_space 字样。

2. 查看线程栈

要想查看所有线程的线程栈,可以使用 ~*e !dumpstack 命令。

  • 搜索 WaitUntilGCComplete 关键字。

从图中看,嘿嘿,真的有18个线程在等待,而且还看到了 System.String.Concat ,是不是和我上上篇发的 his cpu爆高是一个套路?????????????

  • 搜索 try_allocate_more_space 关键字。

我去,竟然没有 try_allocate_more_space 关键词,这就和 his 不是一个套路了,???????????? 有可能这个dump踩的不是特别好的时机,有可能程序正处于某些怪异行为中。

看样子这段路走到头了,不过还是那句话,线程是靠cpu养着的,那就硬着头皮看看各个线程都在做什么吧,为了让结果更清晰一点,换一个命令 ~*e !clrstack

从图中可以看出当前有 25 个线程正卡在 FindEntry(System.__Canon) 处,而且从调用堆栈上看,貌似是 aliyun 封装的dll,为什么有这么多的线程卡在这里呢?这就给人一个很大的问号?接下来我就把阿里云的这段代码给导出来看看到底发生了什么。

3. 查看问题代码

要想导出问题代码,还是用经典的 !ip2md + !savemodule 组合命令。


0:000> !ip2md 000007fe9a1a0641
MethodDesc:   000007fe9a5678e0
Method Name:  Aliyun.Acs.Core.Utils.CacheTimeHelper.AddLastClearTimePerProduct(System.String, System.String, System.DateTime)
Class:        000007fe9a595a08
MethodTable:  000007fe9a567900
mdToken:      00000000060000a6
Module:       000007fe9a561f58
IsJitted:     yes
CodeAddr:     000007fe9a1a0610
Transparency: Critical
0:000> !savemodule 000007fe9a561f58 E:\dumps\AddLastClearTimePerProduct.dll
3 ps in file
p 0 - VA=2000, VASize=14148, FileAddr=200, FileSize=14200
p 1 - VA=18000, VASize=3fc, FileAddr=14400, FileSize=400
p 2 - VA=1a000, VASize=c, FileAddr=14800, FileSize=200

然后用 ILSpy 反编译一下这个dll,因为是阿里云的代码,我就可以放心大胆的放出来啦。


// Aliyun.Acs.Core.Utils.CacheTimeHelper
using System;
using System.Collections.Generic;public class CacheTimeHelper
{private static Dictionary<string, DateTime> lastClearTimePerProduct = new Dictionary<string, DateTime>();private const int ENDPOINT_CACHE_TIME = 3600;public static bool CheckCacheIsExpire(string product, string regionId){string key = product + "_" + regionId;DateTime dateTime;if (lastClearTimePerProduct.ContainsKey(key)){dateTime = lastClearTimePerProduct[key];}else{dateTime = DateTime.Now;lastClearTimePerProduct.Add(key, dateTime);}if (3600.0 < (DateTime.Now - dateTime).TotalSeconds){return true;}return false;}public static void AddLastClearTimePerProduct(string product, string regionId, DateTime lastClearTime){string key = product + "_" + regionId;if (lastClearTimePerProduct.ContainsKey(key)){lastClearTimePerProduct.Remove(key);}lastClearTimePerProduct.Add(key, lastClearTime);}
}

可以看出,上面这段代码在 if (lastClearTimePerProduct.ContainsKey(key)) 处走不下去了,如果往下追,可参考 Dictionary 的源码。


public class Dictionary<TKey, TValue> 
{// System.Collections.Generic.Dictionary<TKey,TValue>public bool ContainsKey(TKey key){return FindEntry(key) >= 0;}
}

到这里,有没有看出这个 CacheTimeHelper 有什么问题吗?对,竟然在多线程环境下用的是非线程安全的 Dictionary<string, DateTime>,这就很有问题了。

4. 用 Dictionary 到底会有什么问题

在多线程环境下用 Dictionary 肯定会导致数据错乱,这个毫无疑问,而且还会遇到一些 迭代时异常,但如果说这个误用会导致 CPU 爆高,在我的视野范围内还没看到过。。。为了确保起见,到 bing 上搜搜这样的 天涯沦落人

嘿嘿,还真的有这样的案例:High CPU in .NET app using a static Generic.Dictionary (

https://www.tessferrandez.com/blog/2009/12/21/high-cpu-in-net-app-using-a-static-generic-dictionary.html

),再截个图。

从文章描述看,简直是一摸一样????????????,这也就断定在多线程环境下操作 Dictionary ,可能会导致 FindEntry(key) 时出现死循环,然后 25 个死循环一起把cpu抬起来了,补充一下当前爆满的CPU利用率。。。


0:000> !tp
CPU utilization: 100%
Worker Thread: Total: 27 Running: 27 Idle: 0 MaxLimit: 32767 MinLimit: 4
Work Request in Queue: 0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 4 Free: 3 MaxFree: 8 CurrentLimit: 3 MaxLimit: 1000 MinLimit: 4

三:总结

既然是阿里云的sdk出的bug,这问题就麻烦了。。。改也改不得,然后告诉朋友去提工单解决。

本以为事情就这样结束了,但我想一想,几年前用的阿里云其他 sdk 也遇到了类似CPU爆高的问题,后来通过升级sdk就搞定了,这次也赌赌看,先看一下程序集信息。


[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("Alibaba Cloud")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("©2009-2018 Alibaba Cloud")]
[assembly: AssemblyDescription("Alibaba Cloud SDK for C#")]
[assembly: AssemblyFileVersion("1.1.12.0")]
[assembly: AssemblyInformationalVersion("1.1.12")]
[assembly: AssemblyProduct("aliyun-net-sdk-core")]
[assembly: AssemblyTitle("aliyun-net-sdk-core")]
[assembly: AssemblyVersion("1.1.12.0")]

可以看到朋友当前用的是 1.1.12.0 版本,那就把 aliyun-net-sdk-core 升级到最新再看看这个 CacheTimeHelper 有没有被修复 ?

果然不出所料,在新版本中给修复好了,所以经验告诉我,用阿里云的sdk,要记得经常升级,不然各种大坑等着你。。。????????????

END

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

1. CPU爆高

2. 内存暴涨

3. 资源泄漏

4. 崩溃死锁

5. 程序呆滞

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

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

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

相关文章

java swing 示例_JAVA简单Swing图形界面应用演示样例

JAVA简单Swing图形界面应用演示样例package org.rui.hello;import javax.swing.JFrame;/*** 简单的swing窗体* author lenovo**/public class HelloSwing {public static void main(String[] args) {JFrame framenew JFrame("hello Swing");frame.setDefaultCloseOpe…

.NET上海社区线下Meetup - 5.22 Blazor Day

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

入门机器学习,开启人工智能大门!

AI这个词相信大家都非常熟悉&#xff0c;近几年来人工智能圈子格外热闹&#xff0c;光是AlphoGo就让大家对它刮目相看。今天小天就来跟大家唠一唠如何进军人工智能的第一步——机器学习。在机器学习领域&#xff0c;Python已经成为了主流。一方面因为这门语言简单易上手&#x…

java集合框架的结构_集合框架(Collections Framework)详解及代码示例

简介集合和数组的区别&#xff1a;数组存储基础数据类型&#xff0c;且每一个数组都只能存储一种数据类型的数据&#xff0c;空间不可变。集合存储对象&#xff0c;一个集合中可以存储多种类型的对象。空间可变。严格地说&#xff0c;集合是存储对象的引用&#xff0c;每个对象…

Unity3D OpenVR 虚拟现实 保龄球打砖块游戏开发

据说水哥买了 Valve Index 设备&#xff0c;既然这个设备这么贵&#xff0c;不开发点有&#xff08;zhi&#xff09;趣&#xff08;zhang&#xff09;游戏就感觉对不起这个设备。本文将来开始着手开发一个可玩性不大&#xff0c;观赏性极强的保龄球打砖块游戏。这仅仅只是一个入…

mac mysql 移动硬盘_MAC一些高能过程记录(一些没必要的坑)

搞计算机的&#xff0c;谁电脑上没个数据库&#xff0c;不管用不用的着&#xff0c;有时候总需要&#xff0c;比如调试下博客呀之类的, 毕竟一般都会觉得数据库很好玩啊1.MySql安装&#xff1a;dmg、pkg什么的直接装吧&#xff0c;结束后会给你一个提示&#xff0c;上面会有密…

数学思维比数学运算更重要

全世界只有3.14 % 的人关注了数据与算法之美数学的证明依靠严密的逻辑推理&#xff0c;一经证明就永远正确&#xff0c;所以&#xff0c;数学证明是绝对的。相对而言&#xff0c;科学的证明则依赖于观察、实验数据和理解力&#xff0c;科学理论的证明难以达到数学定理证明所具有…

多年前那些优秀的工程师,后来都去哪儿了?

这是头哥侃码的第241篇原创上周末&#xff0c;我读初中的儿子突然问我&#xff1a;“爸爸&#xff0c;你是不是从好买离职了&#xff1f;”我听完&#xff0c;忙惊讶地问他是怎么知道的。他朝我做了个鬼脸&#xff0c;然后指了指我的手机说&#xff1a;“你的文章写的如此生动&…

使用easyUI 格式化datagrid列

author YHC 以下示例格式化在easyui DataGrid 里的列数据,和使用自定义列formatter ,如果价格小于20就将文本变为红色. 查看 Demo 格式化一个DataGrid 列,我们需要设置formatter 属性它是一个函数,这个格式化函数包含三个参数: value: 当前列对应字段值.row: 当前的row(行)记录…

收藏 | 分享 3 种脑洞大开的Excel技巧

全世界只有3.14 % 的人关注了数据与算法之美身为职场人&#xff0c;Excel基本是每天都会打开的软件&#xff0c;如果把对它的使用熟练程度分个等级&#xff0c;大概可以分为几下几种&#xff1a;Level 1&#xff1a;对Excel的基本功能已经有所了解&#xff0c;但还不熟练&#…

yaml for java_细数Java项目中用过的配置文件(YAML篇)

灵魂拷问&#xff1a;YAML&#xff0c;在项目中用过没&#xff1f;它与 properties 文件啥区别&#xff1f;目前 SpringBoot、SpringCloud、Docker 等各大项目、各大组件&#xff0c;在使用过程中几乎都能看到 YAML 文件的身影。2017 年的时候&#xff0c;我才真正把 YAML 文件…

超全面!8 种互联网常用生命周期完整指南~

什么是生命周期&#xff1f; 百度给出的定义是&#xff1a;生命周期就是指一个对象的生老病死。 生命周期的概念应用很广泛&#xff0c;特别是在政治、经济、环境、技术、社会等诸多领域经常出现&#xff0c;其基本涵义可以通俗地理解为“从摇篮到坟墓”的整个过程。对于某个…

技术分享|集成开放平台使用Consul Watch机制实现配置热更新

源宝导读&#xff1a;在微服务架构体系中&#xff0c;由于微服务众多&#xff0c;服务之间又有互相调用关系&#xff0c;因此&#xff0c;一个通用的分布式配置管理是必不可少的。本文将介绍如何使用Consul Watch机制实现配置集中管理与热更新。前言随着程序功能的日益复杂&…

14个超有料的优质公众号,关注了就舍不得删

全世界有3.14 % 的人已经关注了数据与算法之美关注了那么多公众号&#xff0c;百无聊奈地看文章你是否觉得时间被浪费&#xff0c;生命被辜负了&#xff1f;在号的数量上做减法&#xff0c;质量上做加法接下来给大家推荐最近一直在阅读的几个优质公众号每一篇推文都值得你点开长…

python新手入门项目推荐_推荐:一个适合于Python新手的入门练手项目

随着人工智能的兴起&#xff0c;国内掀起了一股Python学习热潮&#xff0c;入门级编程语言&#xff0c;大多选择Python&#xff0c;有经验的程序员&#xff0c;也开始学习Python&#xff0c;正所谓是人生苦短&#xff0c;我用Python有个Python入门练手项目&#xff0c;一直没有…

基于虹软人脸识别,实现RTMP直播推流追踪视频中所有人脸信息(C#)

大家应该都知道几个很常见的例子&#xff0c;比如在张学友的演唱会&#xff0c;在安检通道检票时&#xff0c;通过人像识别系统成功识别捉了好多在逃人员&#xff0c;被称为逃犯克星&#xff1b;人行横道不遵守交通规则闯红灯的路人被人脸识别系统抓拍放在大屏上以示警告&#…

这些数据获取方式,一般人不知道

全世界只有3.14 % 的人关注了数据与算法之美在这个用数据说话的时代&#xff0c;能够打动人的往往是用数据说话的理性分析&#xff0c;无论是对于混迹职场的小年轻&#xff0c;还是需要数据进行分析和研究的同学&#xff0c;能够找到合适的数据源都是非常重要的。特别是想要对一…

ftp 笔记

Ubuntu自带wget。如果没有密码的ftp&#xff0c;直接wget ftp://111.222.33.4/path/filename&#xff0c;就可以下载ftp://111.222.33.4的文件夹path里的文件filename。如果是有密码的ftp&#xff0c;则wget ftp://username:passwordftp.111.222.33.4/path/filename。如果用的…

java定义构造方法_JAVA基础学习之路(三)类定义及构造方法

类的定义及使用一&#xff0c;类的定义classBook {//定义一个类intprice;//定义一个属性intnum;public static int getMonney(int price, intnum) {//定义一个方法return price*num;}}public classtest2 {public static voidmain(String args[]) {Book monney newBook();//声明…

通过Dapr实现一个简单的基于.net的微服务电商系统(十一)——一步一步教你如何撸Dapr之自动扩/缩容...

上一篇我们讲到了dapr提供的bindings&#xff0c;通过绑定可以让我们的程序轻装上阵&#xff0c;在极端情况下几乎不需要集成任何sdk&#xff0c;仅需要通过httpclienttext.json即可完成对外部组件的调用&#xff0c;这样只需要对外暴露一个轻量级的http服务器提供restapi即可作…