記一次 .NET某報關係統 非托管泄露分析

来源:https://www.cnblogs.com/huangxincheng/archive/2023/08/08/17614463.html
-Advertisement-
Play Games

## 一:背景 ### 1. 講故事 前段時間有位朋友找到我,說他的程式記憶體會出現暴漲,讓我看下是怎麼事情?而且還告訴我是在 Linux 環境下,說實話在Linux上分析.NET程式難度會很大,難度大的原因在於Linux上的各種開源工具主要是針對 C/C++, 和 .NET 一毛錢關係都沒有,說到底 ...


一:背景

1. 講故事

前段時間有位朋友找到我,說他的程式記憶體會出現暴漲,讓我看下是怎麼事情?而且還告訴我是在 Linux 環境下,說實話在Linux上分析.NET程式難度會很大,難度大的原因在於Linux上的各種開源工具主要是針對 C/C++, 和 .NET 一毛錢關係都沒有,說到底微軟在 Linux 上的調試領域支持度還遠遠不夠。

雖然知道分析起來難度可能會很大,但該分析還是要分析的,讓朋友抓一個 dump 過來,上 WinDbg 說話。

二:WinDbg 分析

1. 到底是哪裡的泄露

只要是進程都會有記憶體段的,所以分析Linux的dump一樣可以使用 !address -summary 命令來觀察。


0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown>                              1607 ffffffff`cd7a9e00 (  16.000 EB) 100.00%  100.00%
Image                                 41699        0`31e57200 ( 798.340 MB)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
                                       1247 fffffffe`1c910000 (  16.000 EB)          100.00%
MEM_PRIVATE                           42059        1`e2cf1000 (   7.544 GB)   0.00%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
                                       1247 fffffffe`1c910000 (  16.000 EB) 100.00%  100.00%
MEM_COMMIT                            42059        1`e2cf1000 (   7.544 GB)   0.00%    0.00%

--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE                        41067        1`cff54000 (   7.249 GB)   0.00%    0.00%
PAGE_READONLY                           644        0`07268000 ( 114.406 MB)   0.00%    0.00%
PAGE_EXECUTE_READ                       223        0`06d1f000 ( 109.121 MB)   0.00%    0.00%
PAGE_EXECUTE_WRITECOPY                  125        0`04e16000 (  78.086 MB)   0.00%    0.00%

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
<unknown>                              7ffc`78f8e000 ffff8003`86672000 (  16.000 EB)
Image                                  7ff8`49102000        0`10000000 ( 256.000 MB)

這裡簡單提一下,我發現有很多朋友搞不清楚這裡的 16.000 EB 是什麼意思,它其實是2的64次方,即程式的用戶態空間的定址範圍。

從卦中的 MEM_COMMIT=7.544G 來看當前提交記憶體不小,接下來用 !eeheap -gc 觀察下托管堆記憶體占用。


0:000> !eeheap -gc

========================================
Number of GC Heaps: 1
----------------------------------------
generation 0 starts at 7ff688f78f10
generation 1 starts at 7ff688484e70
generation 2 starts at 7ff8f7fff000
ephemeral segment allocation context: none
Small object heap
         segment            begin        allocated        committed allocated size         committed size        
    7ff63fffa000     7ff63fffb000     7ff64fe51d80     7ff64fe5d000 0xfe56d80 (266694016)  0xfe63000 (266743808) 
    7ff6772c8000     7ff6772c9000     7ff6872c2d38     7ff6872c8000 0xfff9d38 (268410168)  0x10000000 (268435456)
    7ff74bffe000     7ff74bfff000     7ff75bffdfc0     7ff75bffe000 0xfffefc0 (268431296)  0x10000000 (268435456)
    7ff773ffe000     7ff773fff000     7ff783ffdfc8     7ff783ffe000 0xfffefc8 (268431304)  0x10000000 (268435456)
    7ff849102000     7ff849103000     7ff859101fe8     7ff859102000 0xfffefe8 (268431336)  0x10000000 (268435456)
    7ff8f7ffe000     7ff8f7fff000     7ff907ffce88     7ff907ffe000 0xfffde88 (268426888)  0x10000000 (268435456)
    7ff6872ca000     7ff6872cb000     7ff68a768438     7ff68aa4b000 0x349d438 (55170104)   0x3781000 (58200064)  
Large object heap starts at 7ff907fff000
         segment            begin        allocated        committed allocated size         committed size        
    7ff733ff8000     7ff733ff9000     7ff73aedd058     7ff73aefe000 0x6ee4058 (116277336)  0x6f06000 (116416512) 
    7ff743ffc000     7ff743ffd000     7ff744358f10     7ff744379000 0x35bf10 (3522320)     0x37d000 (3657728)    
    7ff7a3ffe000     7ff7a3fff000     7ff7a9d63ee0     7ff7a9d84000 0x5d64ee0 (97930976)   0x5d86000 (98066432)  
    7ff7bbffe000     7ff7bbfff000     7ff7c3dc1090     7ff7c3de2000 0x7dc2090 (131866768)  0x7de4000 (132005888) 
    7ff907ffe000     7ff907fff000     7ff90f048b30     7ff90f069000 0x7049b30 (117742384)  0x706b000 (117878784) 
Pinned object heap starts at 7ff90ffff000
         segment            begin        allocated        committed allocated size         committed size        
    7ff90fffe000     7ff90ffff000     7ff9102d15b0     7ff9102d2000 0x2d25b0 (2958768)     0x2d4000 (2965504)    
------------------------------
GC Allocated Heap Size:    Size: 0x7f36bca0 (2134293664) bytes.
GC Committed Heap Size:    Size: 0x7f710000 (2138112000) bytes.

從卦中看當前提交記憶體也僅有 2.13G,這和 7.5G 相距甚遠,說明這是最複雜的 非托管記憶體泄漏

2. 非托管泄露分析

作為一個.NET調試者,需要像醫生一樣盡自己最大可能救治病人,那接下來我們的研究方向在哪裡呢?大家需要知道所有的記憶體占用的基本盤都在 虛擬地址 上,結果一搜索,發現有大概 4w+ 的 dll,一個程式怎麼可能會有這麼多動態鏈接庫呢?截圖如下:

既然找到了可疑之處那就繼續挖吧,接下來就是要考慮這個dll是托管代碼創建的還是非托管代碼創建的,用排除法就好了,如果是托管代碼創建的,那就肯定屬於 Assembly 下的某一個module,可以查下載入堆看看。


0:000> !eeheap -loader
Loader Heap:
--------------------------------------
...
Module 00007ff95e265778: Size: 0x0 (0) bytes.
Module 00007ff95e2661e0: Size: 0x0 (0) bytes.
Module 00007ff95e266c48: Size: 0x0 (0) bytes.
Module 00007ff95e2676b0: Size: 0x0 (0) bytes.
Module 00007ff95e268118: Size: 0x0 (0) bytes.
Module 00007ff95e268b80: Size: 0x0 (0) bytes.
Module 00007ff95e2695e8: Size: 0x0 (0) bytes.
Total size:      Size: 0x0 (0) bytes.
--------------------------------------
Total LoaderHeap size:   Size: 0x4bf4b000 (1274327040) bytes total, 0x4da000 (5087232) bytes wasted.
=======================================

0:000> !dumpmodule 00007ff95e2695e8
Name: *75db8939-8b3a-4075-94ac-e9bb52acf9d1#40147-0.dll
Attributes:              PEFile IsInMemory IsFileLayout 
...
MetaData start address:  00007FF85BBCD330 (2068 bytes)

0:000> !DumpAssembly /d 00007ff80d71bba0
Parent Domain:      0000559009249080
Name:               Unknown
ClassLoader:        00007FF80D71BC00
  Module
  00007ff95e2695e8    *75db8939-8b3a-4075-94ac-e9bb52acf9d1#40147-0.dll

從卦中看雖然載入堆只有 1.27G,但它還有很多關聯的記憶體,而且動態module高多4w+,接下來用 !dumpmodule -mt 觀察內部是什麼類型。


0:000> !dumpmodule -mt 00007ff95e2695e8
Name: *75db8939-8b3a-4075-94ac-e9bb52acf9d1#40147-0.dll
Attributes:              PEFile IsInMemory IsFileLayout 
...
Types defined in this module

              MT          TypeDef Name
------------------------------------------------------------------------------
00007ff95e269d80 0x02000004 Submission#0
00007ff95e269ec0 0x02000005 Submission#0+<>d__0

Types referenced in this module

              MT            TypeRef Name
------------------------------------------------------------------------------
00007ff92d6152a8 0x0200000c System.Object
00007ff92ead4d18 0x0200000d System.Threading.Tasks.Task`1
00007ff93133f298 0x0200000e System.Runtime.CompilerServices.IAsyncStateMachine
00007ff92d6cec90 0x0200000f System.Exception
00007ff93133f648 0x02000010 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1

0:000> !dumpmt -md 00007ff95e269ec0
...
MethodDesc Table
           Entry       MethodDesc    JIT Name
00007FF92D620030 00007ff92d615238    JIT System.Object.Finalize()
00007FF92D620038 00007ff92d615248 PreJIT System.Object.ToString()
00007FF92D620040 00007ff92d615258    JIT System.Object.Equals(System.Object)
00007FF92D620058 00007ff92d615298    JIT System.Object.GetHashCode()
00007FF95DFFB0E0 00007ff95e269e58    JIT Submission#0+<>d__0.MoveNext()
00007FF95DFF9B70 00007ff95e269e78   NONE Submission#0+<>d__0.SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)
00007FF95DFF9B60 00007ff95e269e48    JIT Submission#0+<>d__0..ctor()

從卦中看也只能看到一些 Submission 為首碼的類與之相關的狀態機類,也看不出來是誰創建的,結果又入了困境。

3. 到底是誰作的孽

要想獲取動態程式集的創建事件,有一個好辦法就是用跨平臺的 dotnet-trace,讓它捕獲程式集的載入事件即可,詳情可參考:https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/collect-details ,然後讓朋友跑 30min 看看,參考命令如下:


dotnet-trace collect -p 4108 --clrevents loader --duration 00:00:30:00

有了生成好的 dotnet_xxxx.nettrace 之後就可以用 perfview 觀察了,打開 Event視圖,搜索 AssemblyLoad 事件,截圖如下:

通過 Time MSec 的748首碼來看,這1s種能生成幾十個動態程式集,接下來右鍵選擇 Open Any Stacks 觀察是什麼代碼調用的,截圖如下:

從 perfivew 的輸出看,原來是 XXXCusDis 方法內部調用 Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync 生成了非常多的程式集。

最後就是把 CSharpScript.EvaluateAsync 告訴朋友,能不能給剔除掉做個排查?

三:總結

網上查了下 Microsoft.CodeAnalysis.CSharp.Scripting 可以用來生成C#腳本代碼,大家在用的時候小心點吧。

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

-Advertisement-
Play Games
更多相關文章
  • ## 教程簡介 PDFBox是一個開源Java庫,支持PDF文檔的開發和轉換.使用此庫,您可以開發用於創建,轉換和操作PDF文檔的Java程式.除此之外,PDFBox還包括一個命令行實用程式,用於使用可用的PDF對PDF執行各種操作Jar文件. [PDFBox入門教程](https://www.it ...
  • 本文從源碼層面介紹了Spring如何創建bean、如何解決迴圈依賴,同時也介紹了不能解決哪些迴圈依賴,同時在文章的最後解決迴圈依賴報錯的幾個方法 ...
  • 作者:樹洞君 \ 鏈接:https://juejin.cn/post/7064376361334358046 # 事故描述 從6點32分開始少量用戶訪問app時會出現首頁訪問異常,到7點20分首頁服務大規模不可用,7點36分問題解決。 # 整體經過 6:58 發現報警,同時發現群里反饋首頁出現網路繁 ...
  • ## 一. 時間複雜度 - 時間複雜度簡單的說就是一個程式運行所消耗的時間,叫做時間複雜度,我們無法目測一個程式具體的時間複雜度,但是我們可以估計大概的時間複雜度。 - 一段好的代碼的就根據演算法的時間複雜度,即使在大量數據下也能保持高效的運行速率,這也是我們學習演算法的必要性。 ### 1.1 大O表 ...
  • ### 寫在前面 也許現在的你需要用PB完成畢業設計、需要維護遠古時代的代碼,又或者是你呆的公司就是要求要用PB開發項目。 不管你是出於什麼原因還在使用PB,不可否認PB在數據視窗非常優秀,熟練使用之後開發資料庫相關的應用非常高效 但由於PB這一框架出現得比較早,而且主要用於傳統基於資料庫得CS開發 ...
  • # UnityStats 屬性詳解 UnityStats 是 Unity 引擎提供的一個用於監測游戲性能的工具,它提供了一系列的屬性值,可以幫助開發者解游戲的運行情況,從而進行優化。本文將詳細介紹 UnityStats 的每個屬性值,並提供多個使用例子幫助開發者更好地使用 UnityStats。 # ...
  • 根據客戶需求,要在TreeView目錄樹上顯示10萬+個節點,但是目錄樹顯示10萬加節點後,整個頁面操作起來非常卡,所以給目錄樹增加了虛擬化設置。但是虛擬化設置一直沒生效,後來經過排查發現是使用的自定義滾動條導致了虛擬化設置沒有生效,後來自己寫了一個滾動條樣式,問題解決了。 目錄樹虛擬化設置屬性 W ...
  • [toc] # MiniMalAPi - 最小的api, 請求都寫在Program.cs中, 可以做微服務 ## Demo ### Program.cs ```c# //基本請求 app.MapGet("/GetTest", () => new { result = "123", code = 20 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...