記一次 .NET 某RFID標簽管理系統 CPU 暴漲分析

来源:https://www.cnblogs.com/huangxincheng/archive/2022/07/22/16504426.html
-Advertisement-
Play Games

一:背景 1. 講故事 前段時間有位朋友說他的程式 CPU 出現了暴漲現象,由於程式是買來的,所以問題就比較棘手了,那既然找到我,就想辦法幫朋友找出來吧,分析下來,問題比較經典,有必要和大家做一下分享。 二:WinDbg 分析 1. CPU 真的爆高嗎 一直關註這個系列的朋友應該知道,用 !tp 驗 ...


一:背景

1. 講故事

前段時間有位朋友說他的程式 CPU 出現了暴漲現象,由於程式是買來的,所以問題就比較棘手了,那既然找到我,就想辦法幫朋友找出來吧,分析下來,問題比較經典,有必要和大家做一下分享。

二:WinDbg 分析

1. CPU 真的爆高嗎

一直關註這個系列的朋友應該知道,用 !tp 驗證即可。


0:161> !tp
CPU utilization: 81%
Worker Thread: Total: 486 Running: 486 Idle: 0 MaxLimit: 8191 MinLimit: 24
Work Request in Queue: 0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 6 Free: 1 MaxFree: 48 CurrentLimit: 6 MaxLimit: 1000 MinLimit: 24

果然 CPU =81% ,並且當前的 481 個工作線程全部打滿,以經驗看可能是遇到鎖什麼的,不過還是先從是否觸發 GC 看起。

2. 是觸發 GC 了嗎?

要查看是否觸發 GC,可以用 !t -special 看看是否有 SuspendEE 字樣。


0:161> !t -special
ThreadCount:      604
UnstartedThread:  0
BackgroundThread: 587
PendingThread:    0
DeadThread:       11
Hosted Runtime:   no
                          
         OSID Special thread type
       29 2e74 DbgHelper 
       30 1014 GC SuspendEE 
       31 4a84 GC 
       32 4a48 GC 
       ...
       52 37c0 GC 
       53 47a0 GC 
       54 4620 Finalizer 
       55 1aa4 ProfilingAPIAttach
       ...

從卦中看,30號線程果然掛了 SuspendEE,並且還是一個 GC 線程,接下來切過去看看此時 GC 正在做什麼?


0:161> ~~[1014]s
eax=00000000 ebx=0724fc10 ecx=00000000 edx=00000000 esi=00000000 edi=0724fc10
eip=77ddf02c esp=0724fbd0 ebp=0724fc34 iopl=0         nv up ei pl nz ac po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
ntdll!NtDelayExecution+0xc:
77ddf02c c20800          ret     8
0:030> k
 # ChildEBP RetAddr      
00 0724fc34 758345da     ntdll!NtDelayExecution+0xc
01 0724fc34 738a74eb     KERNELBASE!SleepEx+0x8a
02 0724fc78 73a0f710     clr!EESleepEx+0x59
03 0724fc78 73a0f809     clr!SVR::gc_heap::mark_steal+0x27c
04 0724fcd0 73a17930     clr!SVR::gc_heap::mark_phase+0x3d0
05 0724fd0c 73a17dc9     clr!SVR::gc_heap::gc1+0xf2
06 0724fd5c 73a174a1     clr!SVR::gc_heap::garbage_collect+0x746
07 0724fd78 73a10d7e     clr!SVR::gc_heap::gc_thread_function+0x14a
08 0724fd98 73a10d0f     clr!SVR::gc_heap::gc_thread_stub+0x72
09 0724fdac 750e62c4     clr!GCThreadStub+0x1f
0a 0724fdc0 77dd1f69     kernel32!BaseThreadInitThunk+0x24
0b 0724fe08 77dd1f34     ntdll!__RtlUserThreadStart+0x2f
0c 0724fe18 00000000     ntdll!_RtlUserThreadStart+0x1b

從卦中的 gc_thread_function 函數看,這是一個阻塞版的 GC 線程,當前正處於 mark_phase 標記階段,並且還在搶其他 GC 線程的活,有點意思。。。

既然是觸發了 GC ,那就看下觸發了哪一代以及什麼原因觸發的。


0:030> x clr!*gc_heap::settings*
73f15da8          clr!WKS::gc_heap::settings = <no type information>
73f13520          clr!SVR::gc_heap::settings = <no type information>
0:030> dp 73f13520 
73f13520  00002df0 00000002 00000001 00000001
73f13530  00000000 00000000 00000000 00000000
73f13540  00000000 00000000 00000000 00000000
73f13550  00000000 00000000 00000005 00000001
73f13560  00000000 00000000 00000000 00000001
73f13570  00000000 0000005a 00000000 00000001
73f13580  05f71b40 86b2ee2e 00040000 00000001
73f13590  00000002 00002000 00000002 00000000

從卦中的 0000000200000005 可知,當前觸發的是 2代 GC,原因是 5,那 5 是什麼意思? 可以看下 clr 中的 gc_reason 即可。


enum gc_reason
{
    reason_alloc_soh = 0,
    reason_induced = 1,
    reason_lowmemory = 2,
    reason_empty = 3,
    reason_alloc_loh = 4,
    reason_oos_soh = 5,
    reason_oos_loh = 6,
    reason_induced_noforce = 7, // it's an induced GC and doesn't have to be blocking.
    reason_gcstress = 8,        // this turns into reason_induced & gc_mechanisms.stress_induced = true
    reason_lowmemory_blocking = 9,
    reason_induced_compacting = 10,
    reason_lowmemory_host = 11,
    reason_pm_full_gc = 12, // provisional mode requested to trigger full GC
    reason_lowmemory_host_blocking = 13,
    reason_bgc_tuning_soh = 14,
    reason_bgc_tuning_loh = 15,
    reason_bgc_stepping = 16,
    reason_max
};

也就是上面的 reason_oos_soh,表示當前的小對象堆中的段空間滿了,那是不是呢? 可以用 !eeheap -gc 看下托管堆。


0:030> !eeheap -gc
Number of GC Heaps: 24
------------------------------
Heap 0 (06d00138)
generation 0 starts at 0xe8a380ec
generation 1 starts at 0xe8a380e0
generation 2 starts at 0x07311000
ephemeral segment allocation context: (0xe8a380f8, 0xe8a38104)
 segment     begin  allocated      size
07310000  07311000  0830fd5c  0xffed5c(16772444)
a86a0000  a86a1000  a969fd10  0xffed10(16772368)
e8a10000  e8a11000  e8a380f8  0x270f8(159992)
Large object heap starts at 0x1f311000
 segment     begin  allocated      size
1f310000  1f311000  1f4cafb0  0x1b9fb0(1810352)
Heap Size:       Size: 0x21deb14 (35515156) bytes.
------------------------------
...
Heap 22 (06d76910)
generation 0 starts at 0xbfd5d228
generation 1 starts at 0xbfd5ce20
generation 2 starts at 0x1d311000
ephemeral segment allocation context: (0xbfd5d234, 0xbfd5d240)
 segment     begin  allocated      size
1d310000  1d311000  1e30fe64  0xffee64(16772708)
bed60000  bed61000  bfd5d234  0xffc234(16761396)
Large object heap starts at 0x2a311000
 segment     begin  allocated      size
2a310000  2a311000  2a311010  0x10(16)
Heap Size:       Size: 0x1ffb0a8 (33534120) bytes.
...
------------------------------
GC Heap Size:    Size: 0x2f6f6d18 (795831576) bytes.

可以看到,heap 上很多都是 segment=16M 打滿狀態,停。。。為什麼 segment 只有 16M ,出現了一個重大線索。

3. 重大線索解讀

一個 Server 版的 GC,擁有高達 24 個邏輯核,居然只有 16M 的 segment,這麼小的 segment,很容易被一些快進快出的大記憶體操作給打滿,也就更容易造成 GC 觸發,而且還是 Full GC,版本信息如下:


0:030> !eeversion
4.7.3416.0 retail
Server mode with 24 gc heaps
SOS Version: 4.7.3416.0 retail build

接下來到線程棧上找找有沒有快進快出的大記憶體操作。

4. 尋找大記憶體操作

既然有快進快出的操作,在 GC 觸發時肯定還會躺在 托管堆 上,我們就從這裡入手。


0:030> !dumpheap -stat
Statistics:
      MT    Count    TotalSize Class Name
...
06cd1750   638727     15899642      Free
30361270   171401     19196912 xxxx.Entities.ProductInventoryLog
30360f90   537090     32225400 xxxx.Entities.ProductInventoryEpcDetail
30f9a148   171404     67875600 System.Data.Entity.Core.Objects.StateManagerValue[]
3036074c   694875     97282500 xxxx.Entities.ProductBorrow
727efd60  8419815    394836372 System.String
Total 14577631 objects
Fragmented blocks larger than 0.5 MB:
    Addr     Size      Followed by
9b0bf500    0.5MB         9b141878 System.Byte[]

從托管堆看,有不少的類對象,接下來抽一個 xxxx.Entities.ProductInventoryEpcDetail 看下引用,然後查根對象的 size。

0:161> !gcroot a8780ecc
Thread 40b8:
    df90d330 715d143a System.Linq.Enumerable+<JoinIterator>d__38`4[[System.__Canon, mscorlib],[System.__Canon, mscorlib],[System.__Canon, mscorlib],[System.__Canon, mscorlib]].MoveNext()
        esi: 
            ->  090eeba4 System.Linq.Enumerable+<JoinIterator>d__38`4[]
                ...
            ->  a8780e80 xxxx.Entities.ProductInventoryEpcDetail[]
            ->  a8780ecc xxxx.Entities.ProductInventoryEpcDetail

Found 1 unique roots (run '!GCRoot -all' to see all roots).

其實這個引用鏈特別長,用 !objsize 090eeba4 顯示對象大小,一直都是卡住中,size 肯定不小, 接下來我們切入到 40b8 看下這個方法,可以發現一個非常複雜的 EF 寫法,又是 outer,又是 inner,又要再關聯,截圖如下:

到這裡大對象操作終於找到了,我發現還有其他方法也有一些 EF 複雜操作,就不一一列舉了。

三:總結

這個 dump 給我們兩個教訓:

  1. 當 sql 很複雜時,千萬不要用 EF 去寫,這中間會產生多少個臨時對象你真的搞不清楚,你也hold不住,建議直接改成 sql,簡單粗暴。

  2. 程式儘量用 64bit 部署,否則你的 segment 會太小,太容易讓 GC 上頭了。

圖片名稱
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 嗨嘍~大家好呀,這裡是魔王吶 VIP音樂下載不了?只能試聽?甚至聽不了? 那麼今天我就來教你怎麼用Python白嫖VIP音樂~ 所需準備 第三方庫: requests >>> pip install requests 開發環境: 版 本: python 3.8 編輯器:pycharm 2021 ...
  • 多商戶商城系統,也稱為B2B2C(BBC)平臺電商模式多商家商城系統。可以快速幫助企業搭建類似拼多多/京東/天貓/淘寶的綜合商城。 多商戶商城系統支持商家入駐加盟,同時滿足平臺自營、旗艦店等多種經營方式。平臺可以通過收取商家入駐費,訂單交易服務費,提現手續費,簡訊通道費等多手段方式,實現整體盈利。 ...
  • 5 如何合理使用索引加速 tips: 500萬條建表sql參照網盤sql腳本 [root@linux-141 bin]# ./mysql -u root -p itcast < product_list-5072825.sql 索引是資料庫優化最常用也是最重要的手段之一, 通過索引通常可以幫助用戶解 ...
  • phpstorm2022是一款非常好用的php開發軟體,軟體支持所有PHP語言功能,提供最優秀的代碼補全、重構、實時錯誤預防等等功能,能夠為程式員提供更為效率的php開發,新版本改進了phpstorm軟體的自動完成功能。還增加了代碼清理工具,可以刪除不必要的部分來優化全類名稱,從而更好的提高用戶的工 ...
  • Mybatis Generator 使用xml配置文件形式自動生成 只生成實體類、mapper介面及mapper.xml。並且包含豐富的內容 首先添加mybatis依賴和相關插件 <!-- 依賴MyBatis核心包 --> <dependencies> <dependency> <groupId>o ...
  • 技術群里有人發了一段代碼: 附言:兄弟們,這個單例怎麼樣? 我回覆:什麼鬼,看不懂啊?! 也有其他小伙伴表示看不懂,看來大家的C#基礎和我一樣並不全面。 我看不懂,主要是因為我沒用過TaskCompletionSource和Interlocked的CompareExchange方法,然後經過我1、2 ...
  • 1.Xshell遠程登錄Linux系統 在實際的項目部署工作中,遠程登錄到伺服器上是繞不開的彎。本文遠程登錄Linux系統選用工具的是目前最常用、最好用的Xshell。Xsheel是一個強大的安全終端模擬軟體,它支持SSH1、SSH2以及Windows系統的Telnet協議。它的運行速度流程並且完美 ...
  • 1.保障應用程式埠的連通性 通常情況下伺服器的防火牆通常都是開啟的狀態,所以我們需要保證我們部署應用程式的埠是開啟了相應的訪問許可權,否則我們的應用程式將無法被外界進行訪問。這裡為了快速測試應用程式的埠連通性,我們使用比較方便的Telnet工具進行測試,該工具的安裝包內置在Windows操作系統 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...