記一次 .NET 某智慧物流WCS系統CPU爆高分析

一:背景

1. 講故事

哈哈,再次見到物流類軟件,上個月有位朋友找到我,說他的程序出現了 CPU 爆高,讓我幫忙看下什么原因,由于那段時間在苦心研究 C++,分析和經驗分享也就懈怠了,今天就給大家安排上。

話不多說,上 windbg 說話。

二:WinDbg 分析

1. CPU 真的爆高嗎

既然說 CPU 爆高,那就用 !tp 驗證下。

0:000>?!tpMethod?table?is?shared?(not?implemented):?System.Threading.ThreadPool
CPU?utilization:?81?Unknown?format?characterUnknown?format?control?characterWorker?Thread:?Total:?203?Running:?183?Idle:?0?MaxLimit:?300?MinLimit:?150
Work?Request?in?Queue:?0
--------------------------------------
Number?of?Timers:?40
--------------------------------------
Completion?Port?Thread:Total:?21?Free:?21?MaxFree:?80?CurrentLimit:?21?MaxLimit:?300?MinLimit:?150

從卦中看確實 CPU=81%,不過輸出信息很奇怪,方法表都出錯了,猜的不錯應該是觸發 GC 把 托管堆給關閉了,源碼如下:

GCScan::GcRuntimeStructuresValid?(FALSE);plan_phase?(n);GCScan::GcRuntimeStructuresValid?(TRUE);

也可以用 !dumpheap -stat 來驗證。

0:000>?!dumpheap?-stat
The?garbage?collector?data?structures?are?not?in?a?valid?state?for?traversal.
It?is?either?in?the?"plan?phase,"?where?objects?are?being?moved?around,?or
we?are?at?the?initialization?or?shutdown?of?the?gc?heap.?Commands?related?to?
displaying,?finding?or?traversing?objects?as?well?as?gc?heap?segments?may?not?
work?properly.?!dumpheap?and?!verifyheap?may?incorrectly?complain?of?heap?
consistency?errors.
Could?not?request?method?table?data?for?object?000001E49376D520?(MethodTable:?FFFFFFFFFFE026C0).

2. 為什么會觸發 GC

此時我們已知道是 GC 觸發,接下來可以通過 !t + !clrstack 找到那個觸發 GC 的線程,通過線程棧看看正在干嘛 ?

0:000>?!t?
ThreadCount:??????382
UnstartedThread:??0
BackgroundThread:?340
PendingThread:????0
DeadThread:???????0
Hosted?Runtime:???noLock??DBG???ID?????OSID?ThreadOBJ???????????State?GC?Mode?????GC?Alloc?Context??????????????????Domain???????????Count?Apt?Exception0????1?????1ba4?000001E45C018C90????2a020?Preemptive??0000000000000000:0000000000000000?000001e45c368cb0?1?????MTA?297??286?????2144?000001E478521200??1029220?Cooperative?0000000000000000:0000000000000000?000001e45c368cb0?0?????MTA?(GC)?(Threadpool?Worker)?0:297>?!clrstack?
OS?Thread?Id:?0x2144?(297)Child?SP???????????????IP?Call?Site0e?0000002f`2927ade0?00007ffa`afda2096?????coreclr!WKS::gc_heap::garbage_collect+0x2a1?[e:\a\_work\191\s\src\gc\gc.cpp?@?16967]?
0f?0000002f`2927aee0?00007ffa`afdbe746?????coreclr!WKS::GCHeap::GarbageCollectGeneration+0x156?[e:\a\_work\191\s\src\gc\gc.cpp?@?35107]?
10?(Inline?Function)?--------`--------?????coreclr!WKS::gc_heap::try_allocate_more_space+0x1f5?[e:\a\_work\191\s\src\gc\gc.cpp?@?13197]?
11?0000002f`2927af30?00007ffa`afd80c9f?????coreclr!WKS::gc_heap::allocate_more_space+0x216?[e:\a\_work\191\s\src\gc\gc.cpp?@?13490]?
12?(Inline?Function)?--------`--------?????coreclr!WKS::gc_heap::allocate+0x37e?[e:\a\_work\191\s\src\gc\gc.cpp?@?13521]?
13?(Inline?Function)?--------`--------?????coreclr!WKS::GCHeap::Alloc+0x3e5?[e:\a\_work\191\s\src\gc\gc.cpp?@?34419]?
14?(Inline?Function)?--------`--------?????coreclr!Alloc+0x4be?[e:\a\_work\191\s\src\vm\gchelpers.cpp?@?241]?
15?(Inline?Function)?--------`--------?????coreclr!AllocateObject+0x512?[e:\a\_work\191\s\src\vm\gchelpers.cpp?@?1156]?
16?0000002f`2927af90?00007ffa`51c05122?????coreclr!JIT_New+0x5ff?[e:\a\_work\191\s\src\vm\jithelpers.cpp?@?2810]?
...
0000002F2927B228?00007ffaafd63aff?[HelperMethodFrame:?0000002f2927b228]?
0000002F2927B340?00007ffa51c05122?Jint.Native.Object.ObjectInstance..ctor(Jint.Engine)
0000002F2927B380?00007ffa51c058aa?Jint.Native.Array.ArrayConstructor.CreateArrayConstructor(Jint.Engine)
0000002F2927B3D0?00007ffa51c0407c?Jint.Engine..ctor(System.Action`1<Jint.Options>)
...

由于信息比較敏感,我就不過多的輸出了,不過可以看出 GC 的引發是由于 Jint 組件,查了下資料是 JavaScript.NET 用來交互的,為了進一步驗證,觀察下此時 GC 觸發的代以及什么原因。

0:297>?dx?-r1?(*((coreclr!WKS::gc_mechanisms?*)0x7ffab021df90))
(*((coreclr!WKS::gc_mechanisms?*)0x7ffab021df90))?????????????????[Type:?WKS::gc_mechanisms][+0x000]?gc_index?????????:?0x984ab?[Type:?unsigned?__int64][+0x008]?condemned_generation?:?0?[Type:?int][+0x00c]?promotion????????:?1?[Type:?int][+0x010]?compaction???????:?1?[Type:?int][+0x014]?loh_compaction???:?0?[Type:?int][+0x018]?heap_expansion???:?0?[Type:?int][+0x01c]?concurrent???????:?0x0?[Type:?unsigned?int][+0x020]?demotion?????????:?1?[Type:?int][+0x024]?card_bundles?????:?1?[Type:?int][+0x028]?gen0_reduction_count?:?0?[Type:?int][+0x02c]?should_lock_elevation?:?0?[Type:?int][+0x030]?elevation_locked_count?:?0?[Type:?int][+0x034]?elevation_reduced?:?0?[Type:?int][+0x038]?minimal_gc???????:?0?[Type:?int][+0x03c]?reason???????????:?reason_alloc_soh?(0)?[Type:?gc_reason][+0x040]?pause_mode???????:?pause_interactive?(1)?[Type:?WKS::gc_pause_mode][+0x044]?found_finalizers?:?1?[Type:?int][+0x048]?background_p?????:?0?[Type:?int][+0x04c]?b_state??????????:?bgc_not_in_process?(0)?[Type:?bgc_state][+0x050]?allocations_allowed?:?1?[Type:?int][+0x054]?stress_induced???:?0?[Type:?int][+0x058]?entry_memory_load?:?0x0?[Type:?unsigned?int][+0x05c]?exit_memory_load?:?0x0?[Type:?unsigned?int]

從卦中看,當前觸發的是 0 代GC,觸發原因是 0代 的閾值滿了,這是一個很正常的 GC 操作,理應不會造成 CPU 爆高,除非是那些傷害性比較大的 FULLGC,由于沒有更多的 dump 可以參考,到這里就沒法更進一步確認了。

3. 還有其他線索嗎

雖然 .NET 程序大多 CPU 爆高是由于 GC 的頻繁觸發所致,但也有其他情況,比如 CPU 密集型操作往往也會,就像我之前解讀 B站的LUA死循環導致的CPU爆高場景下如何通過 火焰圖 去尋找熱點函數。

那這個 dump 會不會也存在這種情況呢?不管有沒有,在一個 dump 的情況下也只能 死馬當作活馬醫 了,可以用 !runaway 查查當前線程運行時間。

0:297>?!runawayUser?Mode?TimeThread???????Time269:2354?????0?days?0:07:04.171274:15d4?????0?days?0:06:16.453280:1c98?????0?days?0:05:32.406284:438??????0?days?0:04:37.703283:183c?????0?days?0:04:29.531282:122c?????0?days?0:04:24.703288:2060?????0?days?0:03:59.953286:28d0?????0?days?0:03:56.640289:2a84?????0?days?0:03:50.859290:1224?????0?days?0:03:44.640291:2e4c?????0?days?0:03:29.937292:f0c??????0?days?0:03:28.656293:2454?????0?days?0:03:26.640275:2810?????0?days?0:03:23.828294:2f34?????0?days?0:03:22.312295:24ec?????0?days?0:03:17.625297:2144?????0?days?0:03:16.609298:2c34?????0?days?0:03:14.609299:2480?????0?days?0:03:11.218...

線程還是蠻多的,采樣幾個看一下,發現有很多函數與 序列化 有關。

0:269>?!clrstack?
OS?Thread?Id:?0x2354?(269)Child?SP???????????????IP?Call?Site
0000002F080FD658?00007ffacb236124?[HelperMethodFrame:?0000002f080fd658]?
0000002F080FD770?00007ffab11d806b?System.Runtime.Serialization.Formatters.Binary.SizedArray..ctor()?[E:\A\_work\322\s\corefx\src\System.Runtime.Serialization.Formatters\src\System\Runtime\Serialization\Formatters\Binary\BinaryUtilClasses.cs?@?203]
0000002F080FD7A0?00007ffab11d6964?System.Runtime.Serialization.Formatters.Binary.BinaryParser.get_ObjectMapIdTable()?[E:\A\_work\322\s\corefx\src\System.Runtime.Serialization.Formatters\src\System\Runtime\Serialization\Formatters\Binary\BinaryParser.cs?@?57]
0000002F080FD7E0?00007ffa515132c1?System.Runtime.Serialization.Formatters.Binary.BinaryParser.ReadObjectWithMapTyped(System.Runtime.Serialization.Formatters.Binary.BinaryObjectWithMapTyped)?[E:\A\_work\322\s\corefx\src\System.Runtime.Serialization.Formatters\src\System\Runtime\Serialization\Formatters\Binary\BinaryParser.cs?@?532]
0000002F080FD8B0?00007ffab11d74ed?System.Runtime.Serialization.Formatters.Binary.BinaryParser.ReadObjectWithMapTyped(System.Runtime.Serialization.Formatters.Binary.BinaryHeaderEnum)?[E:\A\_work\322\s\corefx\src\System.Runtime.Serialization.Formatters\src\System\Runtime\Serialization\Formatters\Binary\BinaryParser.cs?@?504]0:280>?!clrstack?
OS?Thread?Id:?0x1c98?(280)Child?SP???????????????IP?Call?Site
0000002F185FCE38?00007ffacb236124?[HelperMethodFrame:?0000002f185fce38]?
0000002F185FCF30?00007ffaaf59bb61?System.String.Ctor(Char[],?Int32,?Int32)?[E:\A\_work\191\s\src\mscorlib\shared\System\String.cs?@?79]
0000002F185FCF90?00007ffa5033f984?Newtonsoft.Json.JsonTextReader.ParseReadString(Char,?Newtonsoft.Json.ReadType)
0000002F185FD040?00007ffa5099cd0b?Newtonsoft.Json.JsonTextReader.ReadStringValue(Newtonsoft.Json.ReadType)
0000002F185FD0B0?00007ffa5099cb0e?Newtonsoft.Json.JsonTextReader.ReadAsString()
0000002F185FD0E0?00007ffa514c68fc?Newtonsoft.Json.JsonReader.ReadForType(Newtonsoft.Json.Serialization.JsonContract,?Boolean)0:284>?!clrstack?
OS?Thread?Id:?0x438?(284)Child?SP???????????????IP?Call?Site
0000002F1ED7C9C8?00007ffacb236124?[RedirectedThreadFrame:?0000002f1ed7c9c8]?
0000002F1ED7CA48?00007ffaaf5a6863?System.Buffer.Memmove(Byte*,?Byte*,?UInt64)?[E:\A\_work\191\s\src\mscorlib\src\System\Buffer.cs?@?211]
0000002F1ED7CA50?00007ffaaf59bbb2?System.String.Ctor(Char[],?Int32,?Int32)?[E:\A\_work\191\s\src\mscorlib\shared\System\String.cs?@?83]
0000002F1ED7CAB0?00007ffa5033f984?Newtonsoft.Json.JsonTextReader.ParseReadString(Char,?Newtonsoft.Json.ReadType)
0000002F1ED7CB60?00007ffa5099cd0b?Newtonsoft.Json.JsonTextReader.ReadStringValue(Newtonsoft.Json.ReadType)
0000002F1ED7CBD0?00007ffa5099cb0e?Newtonsoft.Json.JsonTextReader.ReadAsString()

有了線索之后,接下來用 ~*e !clrstack 把所有的線程棧調出來,發現很多的 JsonConvert ,并且還有 5 個線程在做 DeepClone,截圖如下:

0a225206c0d3218e79c221f87df5fb6d.png

接下來把 DeepClone 函數導出來看看,發現是用 BinaryFormatter 來實現對象的深復制。

public?static?T?DeepClone<T>(this?T?obj)?where?T?:?class
{BinaryFormatter?binaryFormatter?=?new?BinaryFormatter();using?MemoryStream?memoryStream?=?new?MemoryStream();binaryFormatter.Serialize(memoryStream,?obj);memoryStream.Seek(0L,?SeekOrigin.Begin);return?(T)binaryFormatter.Deserialize(memoryStream);
}

把發現的這些線索反饋給朋友后,確實也驗證了是 序列化 造成的。

7765f767e4868be071a7bc6f5cc6f538.png

三:總結

分析完畢,這個 dump 給我們的教訓是:

  1. 對象的深復制慎用 BinaryFormatter 這種流式操作,尤其是在大對象的情況下,它是一種 CPU 密集性的,建議采用 AutoMapper 這類 帶 ILEmit, ExpressionTree 還帶編譯緩存的開源工具包。

  2. 高級調試是一場破案之旅,你第一眼看到的往往是程序故意讓你看到的,需要不斷的積累破案經驗練就一雙慧眼。

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

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

相關文章

c#調用存儲過程查詢表并返回影響的行數

// 在此處放置用戶代碼以初始化頁面 String DBConnStr; DataSet MyDataSet new DataSet(); SqlDataAdapter DataAdapter new SqlDataAdapter(); DBConnStr "server192.168.2.120,2433;databaseDB_test;uidsa;pwd43g"; Sq…

采用Atlas+Keepalived實現MySQL讀寫分離、讀負載均衡【轉載】

文章 原始出處 &#xff1a;http://sofar.blog.51cto.com/353572/1601552 一、基礎介紹 1、背景描述 目前我們的高可用DB的代理層采用的是360開源的Atlas&#xff0c;從上線以來&#xff0c;已穩定運行2個多月。無論是從性能上&#xff0c;還是穩定性上&#xff0c;相比其他開…

vscode搭建go開發環境

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、安裝goLang二.配置環境變量三、vscode安裝插件四.安裝golang依賴五.新建go文件前言 能用golang就用golang..這配置很麻煩 提示&#xff1a;以下是本篇文章正…

【GlobalMapper精品教程】024:批量高效實現多種數據格式互轉的方法

globalmapper批量高效實現多種數據格式互轉的方法。 文章目錄一、批量格式轉換二、格式轉換形式舉例一、批量格式轉換 選擇原文件類型&#xff1a; 選擇文件類型&#xff1a; 在源文件列表中添加需要轉換的文件或者文件夾&#xff0c;指定目標文件目錄&#xff0c;文件名稱和投…

基于 WeihanLi.Npoi 實現excel導入時純漢字的日期轉換

基于 WeihanLi.Npoi 實現excel導入時純漢字的日期轉換Intro前段時間有位小伙伴在 Github 上提了一個 “不能識別純漢字的日期格式” issue二〇二二年一月一日 格式的日期單元格識別不出來會變成&#xff0c;0001/1/1 0:00:00 如何讓它能夠識別出來呢&#xff0c;基于 InputForm…

寒假學習筆記(3)

2018.2.9 類 class class 類名{}&#xff1b;類似與結構體&#xff1b;類的實質是一種數據類型&#xff0c;類似于int、char等基本類型&#xff0c;不同的是它是一種復雜的數據類型。因為它的本質是類型&#xff0c;而不是數據&#xff0c;所以不存在于內存中&#xff0c;不能被…

十個模型,總結產品經理溝通方法論

編輯導語&#xff1a;毫不夸張地說溝通占據了產品經理日常工作內容的40%&#xff0c;高效溝通往往能讓事情事半功倍。本文作者結合溝通方法與具體溝通情景講解了如何高效溝通&#xff0c;一起來看看吧&#xff01; 目錄 一、為什么要學會溝通 二、溝通模型 1. PREP原則&…

【Alpha】開發日志Day8-0719

最近幾天是攻堅階段&#xff0c;大家配合得越來越嫻熟了~ 以下是各位的每日小結&#xff1a; 姓名今日完成任務遇到的問題陳劼博寫了一個PPT播放界面&#xff0c;后來發現師兄其實已經完成了黃志華嘗試解決上傳問題,但是沒有成功&#xff1b;寫了一個修改表單的代碼,發現前端寫…

MySQL--字符集

1.字符集概述 簡單的說字符集就是一套文字符號及其編碼、比較規則的集合20世紀60年代初期&#xff0c;美國標準化組織ANSI發布了第一個計算機的字符集ASCII(American Standard Code for Information Interchange)&#xff0c;后來進一步變成了國際標準ISO-646。這個字符集采用7…

【Globalmapper中文入門到精通系列實驗圖文教程】(附配套實驗數據持續更新)

【Globalmapper中文版入門到精通系列實驗圖文教程】&#xff08;附配套實驗數據持續更新&#xff09; 文章目錄一、專欄簡介二、文章目錄三、數據目錄四、傳送門一、專欄簡介 本專欄為GlobalMapper中文入門實戰精品教程&#xff0c;內容主要涉及&#xff1a;Globalmapper23軟件…

【GlobalMapper精品教程】025:影像數據集的建立與巧妙使用

GlobalMapper影像數據集類似于金字塔,作用是提高大量影像的加載與顯示速度,還可批量進行一系列設置。本文的配套數據為data025.rar。 文章目錄 1. 建立影像數據集2. 影像數據集的使用1. 建立影像數據集 (1)點擊【文件】→【創建新地圖目錄】。 (2)選擇影像數據集存放路徑…

使用xUnit為.net core程序進行單元測試(3)

第1部分: http://www.cnblogs.com/cgzl/p/8283610.html 第2部分: http://www.cnblogs.com/cgzl/p/8287588.html 請使用這個項目作為練習的開始: https://pan.baidu.com/s/1ggcGkGb 測試的分組 打開Game.Tests里面的BossEnemyShould.cs, 為HaveCorrectPower方法添加一個Trait屬性…

war部署到tomcat

gs-rest-service-0.1.0.war復制到tomcat-9.0.0.M17\webapps\打開server.xml&#xff0c;這Host節點&#xff0c;加入<Context path"/gs" docBase"gs-rest-service-0.1.0.war" debug"0" privileged"true"/> gs相當于虛擬目錄&…

C# Thread IsBackground作用

背景之前在做一個定時下載任務的時候&#xff0c;使用的是一個主線程在執行任務&#xff1b;后面需求調整了&#xff0c;需要在啟用一個子線程執行優先級更高的單獨通道下載。于是下意識的這么做 new Thread//創建后臺線程Thread bThread new Thread(new ThreadStart(backgrou…

產品經理的分類及術語詳解

一、按項目分類 1、前端型PM 一句話概述&#xff1a;制造口碑帶來流量。 偏用戶體驗&運營&#xff0c;通過極致的產品設計&吸引眼球的產品營銷策略&#xff0c;打造口碑&#xff0c;創造一款用戶量巨大的產品。 【常見術語】 UCD&#xff08;User Centered Design…

Mybatis 攔截器

Mybatis定義了四種攔截器&#xff1a; Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)ParameterHandler (getParameterObject, setParameters)ResultSetHandler (handleResultSets, handleOutputParameters)StatementHandler …

1295 N皇后問題

1295 N皇后問題 時間限制: 2 s 空間限制: 128000 KB 題目等級 : 黃金 Gold 題目描述 Description在nn格的棋盤上放置彼此不受攻擊的n個皇后。按照國際象棋的規則&#xff0c;皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。n后問題等價于再nn的棋盤上放置n個皇后&…

CDN的強大功能

2019獨角獸企業重金招聘Python工程師標準>>> CDN&#xff0c;內容分發網絡&#xff0c;除了用作網站加速外&#xff0c;還能夠更好的保護網站不被攻擊。防護網站不被攻擊的功能成就了CDN運行中的主要責任。CDN 防護原理是其主要在于在相關節點中成功的建立動態加速機…

IDEA創建SpringBoot項目無法連接https://start.spring.io(已解決)

錯誤&#xff1a; 方法&#xff1a; 將&#xff1a;https://start.spring.io 更換為 ?https://start.aliyun.com

論人生自動化

就像設備一樣基本都是由三部分組成&#xff0c;輸入&#xff0c;處理&#xff0c;輸出&#xff0c;三部分。當輸出與輸入兩者有比較&#xff0c;自然就產生了反饋&#xff0c;正反饋或者負反饋&#xff0c;有利于輸出的穩定性。有一些東西或者事情能達到閉環&#xff0c;則一切…