記一次.NET某工控圖片上傳CPU爆高分析

一:背景

1.講故事

今天給大家帶來一個入門級的 CPU 爆高案例,前段時間有位朋友找到我,說他的程序間歇性的 CPU 爆高,不知道是啥情況,讓我幫忙看下,既然找到我,那就用 WinDbg 看一下。

二:WinDbg 分析

1. CPU 真的爆高嗎

其實我一直都在強調,要相信數據,口說無憑,一定要親自驗證一下,可以使用 !tp 命令。

0:000>?!tp
CPU?utilization:?81%
Worker?Thread:?Total:?32?Running:?0?Idle:?18?MaxLimit:?2047?MinLimit:?2
Work?Request?in?Queue:?0
--------------------------------------
Number?of?Timers:?1
--------------------------------------
Completion?Port?Thread:Total:?0?Free:?0?MaxFree:?4?CurrentLimit:?0?MaxLimit:?1000?MinLimit:?2

從卦中可以看到,當前的 CPU=81%,果然爆高無疑,接下來就得調查下為什么會爆高,可以從觸發 GC 入手。

2. GC 觸發了嗎

要觀察是否 GC 觸發,可以觀察下線程列表上是否有 (GC) 字樣,比如下面的輸出。

0:006>?!t
ThreadCount:??????38
UnstartedThread:??0
BackgroundThread:?37
PendingThread:????0
DeadThread:???????0
Hosted?Runtime:???noLock??ID?OSID?ThreadOBJ????State?GC?Mode?????GC?Alloc?Context??Domain???Count?Apt?Exception0????1??5f0?01310688?????2a020?Preemptive??00000000:00000000?0130aa50?0?????MTA?2????2??818?0131e358?????2b220?Preemptive??00000000:00000000?0130aa50?0?????MTA?(Finalizer)?3????6??7b0?01374908???202b220?Preemptive??00000000:00000000?0130aa50?0?????MTA?4????7??f98?01381c50???102a220?Preemptive??00000000:00000000?0130aa50?0?????MTA?(Threadpool?Worker)?6????3??610?013eba78?????2b220?Cooperative?00000000:00000000?0130aa50?1?????MTA?(GC)?9???44??e04?05585068???1029220?Preemptive??00000000:00000000?0130aa50?0?????MTA?(Threadpool?Worker)?10???25??448?063dab30?????21220?Preemptive??00000000:00000000?0130aa50?0?????Ukn?...

從卦中可以看到6號線程果然帶了 (GC) 字樣,接下來用 kb 觀察下到底是哪一代GC。

0:006>?kb#?ChildEBP?RetAddr??????Args?to?Child??????????????
00?05beef18?72bb4825?????0b771000?00000003?00000001?clr!WKS::gc_heap::relocate_survivor_helper+0x87
01?05beef48?72bb46da?????0b771000?00000001?00000000?clr!WKS::gc_heap::relocate_survivors+0x93
02?05beef98?72bb1913?????00000000?00000001?73180140?clr!WKS::gc_heap::relocate_phase+0x8b
03?05bef140?72bb0f69?????00000000?00000001?00000001?clr!WKS::gc_heap::plan_phase+0x13b8
04?05bef168?72bb12ef?????5e7aa9c3?7317fcd0?00000000?clr!WKS::gc_heap::gc1+0xe8
05?05bef1a0?72bb140c?????00000040?7317ff04?7317ff04?clr!WKS::gc_heap::garbage_collect+0x447
06?05bef1c8?72bb161c?????00000000?00000000?00000040?clr!WKS::GCHeap::GarbageCollectGeneration+0x1fb
07?05bef1ec?72bb1696?????7317ff04?71a9d900?00000002?clr!WKS::gc_heap::trigger_gc_for_alloc+0x1e
08?05bef21c?72bff51a?????00000000?00000040?0c1c7aa4?clr!WKS::gc_heap::try_allocate_more_space+0x162
09?05bef230?72bff687?????00000000?01304d38?72bff140?clr!WKS::gc_heap::allocate_more_space+0x18
0a?05bef24c?72ab4477?????013ebab8?00000040?00000002?clr!WKS::GCHeap::Alloc+0x5c
0b?05bef26c?72ab44f5?????01000000?71ab5e90?05bef3f8?clr!Alloc+0x87
0c?05bef2b4?72ab4595?????5e7aab5f?00000bb8?05bef3f8?clr!AllocateObject+0x99
0d?05bef33c?719b8281?????71a2417c?05bef358?05bef35c?clr!JIT_New+0x6b
0e?05bef360?7225652d?????00000000?00000000?00000000?mscorlib_ni!System.Threading.Tasks.Task.Delay+0x41?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs?@?5885]?
0f?05bef454?05a9d18a?????00000000?00000000?00000000?mscorlib_ni!System.Threading.Tasks.Task.Delay+0xd?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs?@?5843]?
...

因為 C++ 默認是 this 協定,從 clr!WKS::gc_heap::plan_phase+0x13b8 方法的第二個參數 00000001 可知,當前觸發了 1 代 GC,其實 1 代 GC 本來就觸發頻繁,所以問題不大,主要就是看是否為 2 代GC,即 FullGC。

到這里,GC觸發的路堵死了,我們就看下是不是還有其他的可疑情況,比如高時鐘個數的線程。

3. 有長時間運行線程嗎

如果是當事人,可以用 Process Explorer 工具直接觀察 Thread 列表的 Cycles Delta 列就能知道,比如下面的百度云管家,

b87a5ecb888dfe7c25104d0aeec7adcd.png

可以看到 11156 號線程占用了太多的時鐘周期個數,可惜我不是當事人,所以只能用 cpuid 命令觀察。

0:006>?!runawayUser?Mode?TimeThread???????Time6:610??????0?days?0:47:07.98410:448??????0?days?0:11:32.53112:17d4?????0?days?0:01:34.2659:e04??????0?days?0:01:29.46811:16ec?????0?days?0:01:11.56213:1458?????0?days?0:01:07.703...

從卦中可以看到,6號線程耗費的時鐘個數遙遙領先,甩了第二名 10 號線程幾條街,這個線程非常可疑,得好好研究下它的托管棧了。

0:006>?!clrstack
OS?Thread?Id:?0x610?(6)
Child?SP???????IP?Call?Site
05bef2d0?72bb47ae?[HelperMethodFrame:?05bef2d0]?
05bef344?719b8281?System.Threading.Tasks.Task.Delay(Int32,?System.Threading.CancellationToken)?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs?@?5885]
05bef36c?7225652d?System.Threading.Tasks.Task.Delay(Int32)?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs?@?5843]
05bef370?05a9d18a?xxx.Api.Core.xxx+c__DisplayClass2_0.<.cctor>b__0()
05bef45c?719b7118?System.Threading.Tasks.Task.InnerInvoke()?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs?@?2884]
05bef468?719b6cc0?System.Threading.Tasks.Task.Execute()?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs?@?2498]
05bef48c?719b70ea?System.Threading.Tasks.Task.ExecutionContextCallback(System.Object)?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs?@?2861]
05bef490?719d40c5?System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,?System.Threading.ContextCallback,?System.Object,?Boolean)?[f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs?@?954]
05bef4fc?719d3fd6?System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext,?System.Threading.ContextCallback,?System.Object,?Boolean)?[f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs?@?902]
05bef510?719b6f68?System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task?ByRef)?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs?@?2827]
05bef574?719b6e72?System.Threading.Tasks.Task.ExecuteEntry(Boolean)?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\Task.cs?@?2756]
05bef584?71a2acbc?System.Threading.Tasks.ThreadPoolTaskScheduler.LongRunningThreadWork(System.Object)?[f:\dd\ndp\clr\src\BCL\system\threading\Tasks\ThreadPoolTaskScheduler.cs?@?49]
05bef588?719a70e3?System.Threading.ThreadHelper.ThreadStart_Context(System.Object)?[f:\dd\ndp\clr\src\BCL\system\threading\thread.cs?@?74]
05bef594?719d40c5?System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,?System.Threading.ContextCallback,?System.Object,?Boolean)?[f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs?@?954]
05bef600?719d3fd6?System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext,?System.Threading.ContextCallback,?System.Object,?Boolean)?[f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs?@?902]
05bef614?719d3f91?System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext,?System.Threading.ContextCallback,?System.Object)?[f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs?@?891]
05bef62c?71a28cae?System.Threading.ThreadHelper.ThreadStart(System.Object)?[f:\dd\ndp\clr\src\BCL\system\threading\thread.cs?@?93]
05bef770?72a90096?[GCFrame:?05bef770]?
05bef954?72a90096?[DebuggerU2MCatchHandlerFrame:?05bef954]

從線程棧上看還好有一個托管方法 xxx.Api.Core.xxx+c__DisplayClass2_0.<.cctor>b__0 ,接下來觀察下源碼,修剪后的代碼如下:

static?xxxUploadPool()
{_queue?=?new?ConcurrentQueue<xxxModel>();_xxx?=?new?xxxService();int?second?=?Configuration.xxx?*?1000;Task.Factory.StartNew(delegate{while?(true){lock?(_queue){if?(_queue.Count?>?0?&&?_queue.TryDequeue(out?var?result)){_xxx.UploadFilexxxx(result._path,?result._repositoryName,?xxx);}}Task.Delay(second);}},?TaskCreationOptions.LongRunning);
}

這段代碼很有意思,它的本來想法就是開啟一個長線程,然后在長線程中不斷的輪詢等待,問題就出在了這個等待上, 即 Task.Delay(second); 這句, 這句代碼起不到任何作用,而且一旦 _queue 中的數據為空就成了死循環, 給 CPU 打滿埋下了禍根。

這里有一個疑問:一個線程能把 CPU 打滿,那太瞧不起CPU 了,肯定是有對等的 core 個數的線程一起發力,打爆CPU,那如何驗證?觀察下 CPU 的個數。

0:006>?!cpuid
CP??F/M/S??Manufacturer?????MHz0??6,85,4??GenuineIntel????31931??6,85,4??GenuineIntel????3193

也就說只要有兩個線程進入了 xxxUploadPool 那就夠了,現象也正是如此。

三:總結

這段代碼確實很有意思,猜測原來就是 Thread.Sleep(second) ,但為了趕潮流改成了 Task.Delay(second),在不清楚后者的使用場景下給 CPU 間歇性爆高埋下了禍根,所以大家在使用新的語法時,一定要弄清楚場景,萬不可生搬硬套。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281205.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281205.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281205.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

微信小程序項目實踐準備工作

微信小程序項目實踐準備工作一、了解微信小程序產品定位及功能介紹微信小程序是一種全新的連接用戶與服務的方式&#xff0c;它可以在微信內被便捷地獲取和傳播&#xff0c;同時具有出色的使用體驗。簡單的說&#xff0c;小程序是微信附屬產品&#xff0c;需要依賴微信&#xf…

VSCode 用戶自定義片段 snippet 基本語法說明

先上一個官方模板&#xff1a; "Print to console": {"prefix": "log","body": ["console.log($1);","$2"],"description": "Log output to console" }prefix 前綴&#xff0c;emmet 觸發條…

Python集合和函數

深淺拷貝&#xff1a;.copy()方法&#xff0c;淺拷貝值拷貝一層。列表中的列表和字典會深拷貝&#xff0c;其他類型會淺拷貝。列表中的列表和字典會隨著副本的修改而改變&#xff0c;其他類型不會隨著副本的改變為改變。ab不是復制&#xff0c;是兩個變量共享同一內存空間&…

從 WinDbg 角度理解 .NET7 的AOT玩法

一&#xff1a;背景 1.講故事前幾天 B 站上有位朋友讓我從高級調試的角度來解讀下 .NET7 新出來的 AOT&#xff0c;畢竟這東西是新的&#xff0c;所以這一篇我就簡單摸索一下。二&#xff1a;AOT 的幾個問題 1. 如何在 .NET7 中開啟 AOT 功能在 .NET7 中開啟 AOT 非常方便&…

.NET Core C#系列之XiaoFeng.ToCast萬能類型轉換器

數據類型相互轉換如&#xff1a;字符串轉整型&#xff0c;字符串轉日期首先要引用 XiaoFeng命名空間下邊三個擴展方法 就是類型轉換的方法最常用的就是 ToCast<T>方法/// <summary> /// 類型相互轉換 /// </summary> /// <typeparam name"T">…

利用 Git OpenSSH 查看/生成 本機 ssh 公鑰

查看本機公鑰 本機公鑰文件一般用戶目錄下的.ssh文件夾&#xff0c;文件夾下有三個文件&#xff0c;分別是 id_rsa 私鑰密碼id_rsa.pub 公鑰內容known_hosts 允許的host地址 使用文本編輯器查看文件id_rsa.pub文件即可 或者使用cat命令查看 # 任意位置打開 Git bash cd ~/.…

高斯消元法

有點線代的知識&#xff1a; const double EPS 1e-8; typedef vector<double> vec; typedef vector<vec> mat; //Ax b vec gauss_jordan(const mat &A, const vec &b) {int n A.size();mat B(n,vec(n1)); //定義大小for(int i0;i < n;i)for(int j0…

判斷對象是否存在某個屬性

JavaScript判斷對象是否存在某個屬性或者方法&#xff0c;常用方法有兩種hasOwnProperty和in hasOwnProperty是Object原型對象上的一個方法&#xff0c;用來判斷對象自身屬性中是否具有指定的屬性。 這個方法可以用來檢測一個對象是否含有特定的自身屬性&#xff1b;和 in 運…

【PPT】適配器模式 和 橋接模式

【PPT】適配器模式 和 橋接模式目錄【PPT】適配器模式 和 橋接模式一、PPT 截圖1.0、封面和目錄1.1、設計模式概述1.2、結構型模式特點1.3、適配器模式1.4、橋接模式二、參考資料及 PPT 獲取方法獨立觀察員 2022 年 11 月 15 日為之前公司準備的分享PPT&#xff0c;后來沒用上。…

Flask 【第七篇】Flask中的wtforms使用

一、簡單介紹flask中的wtforms WTForms是一個支持多個web框架的form組件&#xff0c;主要用于對用戶請求數據進行驗證。 安裝&#xff1a; pip3 install wtforms 二、簡單使用wtforms組件 1、用戶登錄 具體代碼&#xff1a; from flask import Flask,render_template,request,…

CSS自定義滾動條樣式

css通過滾動條偽類來修改滾動條樣式&#xff0c;偽類名稱如下 ::-webkit-scrollbar 滾動條整體部分 ::-webkit-scrollbar-track 滾動條軌道&#xff08;里面裝有滑塊 thumb&#xff09; ::-webkit-scrollbar-thumb 滾動條滑塊 ::-webkit-scrollbar-button 滾動條軌道兩端按鈕 …

為了避免內存攻擊,美國國家安全局提倡Rust、C#、Go、Java、Ruby 和 Swift,但將 C 和 C++ 置于一邊...

本文翻譯自兩篇文章&#xff0c;第一篇是對美國國家安全局在“軟件內存安全”網絡安全信息表的解讀&#xff0c;第二篇是普及什么是內存安全&#xff0c;為什么它很重要&#xff1f;第一篇 為了避免內存攻擊&#xff0c;美國國家安全局提倡Rust、C#、Go、Java、Ruby 和 Swift&a…

自學python(一)

一、入門儀式 學習一門新語言必不可少的一件事&#xff1a; print("Hello world!")二、基礎知識 1、注釋&#xff1a; 單行注釋&#xff1a; 1 print("Hello world!") #輸出Hello world! 多行注釋&#xff1a; 這是多行注釋 這是多行注釋 這是多行注釋…

.NET周報【11月第2期 2022-11-15】

國內文章統一的開發平臺.NET 7正式發布https://www.cnblogs.com/shanyou/archive/2022/11/09/16871945.html在 2020 年規劃的.NET 5功能終于在.NET 7 完成了&#xff0c;為微軟和社區一起為多年來將不同的開發產品統一起來的努力加冕&#xff0c;未來只有一個.NET, 回顧.NET 20…

如何像使用AspNetCore中的Controllers 和 Actions一樣處理MQTT消息

在物聯網項目中&#xff0c; 處理MQTT的topic時費工費力&#xff0c; 代碼一團亂&#xff0c; 什么才是最好的姿勢&#xff1f;這里面我們極力介紹 MQTTnet.AspNetCore.Routing 項目&#xff0c;MQTTnet AspNetCore Routing 是https://github.com/Atlas-LiftTech/MQTTnet.AspN…

chrome 懸停大圖插件_Google Chrome瀏覽器的懸停卡:我不想要的我最喜歡的新東西

chrome 懸停大圖插件If you only have a handful of open tabs in Google Chrome, it’s easy to tell what they are. But as you start to collect more tabs (or make the window smaller), it gets harder. That’s where Hover Cards come in. 如果您在Google Chrome瀏覽器…

GitHub Codespaces 安裝 .NET 7

本文主要介紹如何在 GitHub Codespaces 這個云上 IDE 環境中安裝 .NET 7背景GitHub 的 Codespaces 可以讓我們隨時隨地編寫代碼&#xff0c;一些簡單的修改也非常方便快捷。特別是 .NET 7 發布后&#xff0c;一些可以直接升級的小項目只需要更改配置就可以了&#xff0c;我們可…

chrome怎么隱藏瀏覽器_如何使用Google Chrome的隱藏閱讀器模式

chrome怎么隱藏瀏覽器Chrome 75 has a hidden “Reader” mode that strips web pages down to the bare minimum to make them easier to, well, read. But it’s not enabled by default—here’s how to get it now. Chrome 75具有隱藏的“閱讀器”模式&#xff0c;可將網頁…

angularjs中使用swiper時不起作用,最后出現空白位

controller.js中定義swipers指令&#xff1a; var moduleCtrl angular.module(newscontroller,[infinite-scroll,ngTouch,news.service]) .directive(swipers,swipers); swipers.$inject [$timeout]; function swipers($timeout) {return {restrict: "EA",scope: {…

使用Jupyter記事本記錄和制作.NET可視化筆記

前言&#xff1a;對于記錄筆記的工具特別多&#xff0c;不過對于程序員來說&#xff0c;記錄筆記程序代碼運行結果演示可以同時存在&#xff0c;無疑會極大增加我們的筆記的可讀性和體驗感。以前在寫python的時候&#xff0c;使用jupyter的體驗很好&#xff0c;所以此處做一個基…