記一次 .NET 某制造業 MES 系統崩潰分析

一:背景

1.講故事

前段時間有位朋友微信找到我,說他的程序偶爾會出現內存溢出崩潰,讓我幫忙看下是怎么回事,咨詢了下程序是 x86 部署,聽到這個詞其實心里已經有了數,不管怎么樣還是用 windbg 分析一下。

二:WinDbg 分析

1. x86 程序意味著什么

x86 程序意味著程序默認只能吃到 2G 的內存,或者說只能用 2G 的虛擬地址,這種類型的程序很容易出現 虛擬地址緊張 造成崩潰,那怎么去驗證程序只能吃 2G 內存呢?通常有兩種做法:

1) 使用 !dh 查看 PE 頭

可以用 lm 找到 exe 模塊,然后使用 !dh Device_xxx 觀察 PE 頭,代碼如下:

0:000>?lm
start????end????????module?name
00360000?0099a000???xxxDevice?C?(service?symbols:?CLR?Symbols?without?PDB)????????
157f0000?15abf000???QQPinyin???(export?symbols)???????QQPinyin.ime
....0:000>?!dh?xxxDeviceFile?Type:?EXECUTABLE?IMAGE
FILE?HEADER?VALUES14C?machine?(i386)3?number?of?sections
6305F7E8?time?date?stamp?Wed?Aug?24?18:05:28?20220?file?pointer?to?symbol?table0?number?of?symbolsE0?size?of?optional?header102?characteristicsExecutable32?bit?word?machine
...

最后一行的 32 bit word machine 表示是純純 x86,但在我的分析旅行中,這種也不是特別準,曾經遇到程序開啟了 大地址,最后也只能吃 2G 內存,這就很奇葩了,所以更準的方式就是用 !address 看內存段。

  1. 使用 !address 查看內存段

這種做法萬無一失,輸出如下:

0:000>?!addressBaseAddr?EndAddr+1?RgnSize?????Type???????State?????????????????Protect?????????????Usage
-----------------------------------------------------------------------------------------------
+????????0???360000???360000?????????????MEM_FREE????PAGE_NOACCESS??????????????????????Free???
...
+?7ffe1000?7ffec000?????b000?????????????MEM_FREE????PAGE_NOACCESS??????????????????????Free???????
+?7ffec000?7ffed000?????1000?MEM_PRIVATE?MEM_COMMIT??PAGE_READONLY??????????????????????<unknown>??[HalT............]
+?7ffed000?7fff0000?????3000?????????????MEM_FREE????PAGE_NOACCESS??????????????????????Free???????0:000>???7fff0000?/0x100000
Evaluate?expression:?2047?=?000007ff

卦中最后一個內存段地址為 7fff0000,也就是 2G 的意思,所以最好的辦法就是讓朋友開啟大地址解決,那大地址怎么開,用 anycpu 編譯即可,但有很多朋友反饋用 anycpu 的話,很多 C++ 的鏈接庫會報錯,所以更好的做法是參考這篇:https://www.cnblogs.com/huangxincheng/p/15671957.html

到這里,貌似就可以結案了。。。

2. 真的要讓 2G 地址背鍋嗎

開啟大地址可以讓程序吃到更多的內存,這個不假,但 放之四海而皆準 也不見得,言外之意還得分析下內存是怎么被吃掉的?如果是程序本身的問題,不斷的侵蝕內存,再多的內存也不夠用,對吧。

作為一個負責任的 調試博主,還是不要簡單忽悠過去,接下來用 !address -summary 觀察下內存布局。

0:000>?!address?-summary---?Usage?Summary?----------------?RgnCount?-----------?Total?Size?--------?%ofBusy?%ofTotal
<unknown>??????????????????????????????1221??????????551c4000?(???1.330?GB)??79.82%???66.49%
Free????????????????????????????????????258??????????155dd000?(?341.863?MB)???????????16.69%
Image???????????????????????????????????916???????????b1c6000?(?177.773?MB)??10.42%????8.68%
Stack???????????????????????????????????303???????????62c0000?(??98.750?MB)???5.79%????4.82%
Heap????????????????????????????????????129???????????426f000?(??66.434?MB)???3.89%????3.24%
TEB?????????????????????????????????????101?????????????f7000?(?988.000?kB)???0.06%????0.05%
Other????????????????????????????????????12?????????????60000?(?384.000?kB)???0.02%????0.02%
PEB???????????????????????????????????????1??????????????3000?(??12.000?kB)???0.00%????0.00%---?Type?Summary?(for?busy)?------?RgnCount?-----------?Total?Size?--------?%ofBusy?%ofTotal
MEM_PRIVATE????????????????????????????1437??????????561fd000?(???1.346?GB)??80.77%???67.29%
MEM_IMAGE??????????????????????????????1180???????????d23f000?(?210.246?MB)??12.32%???10.27%
MEM_MAPPED???????????????????????????????66???????????75d7000?(?117.840?MB)???6.91%????5.75%---?State?Summary?----------------?RgnCount?-----------?Total?Size?--------?%ofBusy?%ofTotal
MEM_COMMIT?????????????????????????????2113??????????5ce03000?(???1.451?GB)??87.10%???72.56%
MEM_FREE????????????????????????????????258??????????155dd000?(?341.863?MB)???????????16.69%
MEM_RESERVE?????????????????????????????570???????????dc10000?(?220.062?MB)??12.90%???10.75%...

從輸出看,當前提交內存為:MEM_COMMIT = 1.45G,以我的經驗來說, 1.2G 是一個警戒線,一旦過了,程序崩潰的概率會幾何倍提升。

<unknown>=1.33G 來看,內存可能都被 GC堆 或者 VirtualAlloc 吃掉了,為了進一步驗證,需要看下托管堆。

0:000>?!eeheap?-gc
Number?of?GC?Heaps:?1
generation?0?starts?at?0x5ff45f5c
generation?1?starts?at?0x5fee1000
generation?2?starts?at?0x02d81000
ephemeral?segment?allocation?context:?nonesegment?????begin??allocated??????size
02d80000??02d81000??03d7fc50??0xffec50(16772176)
098c0000??098c1000??0a8bfc10??0xffec10(16772112)
0abf0000??0abf1000??0bbefc90??0xffec90(16772240)
0e0a0000??0e0a1000??0f09ff40??0xffef40(16772928)
13640000??13641000??1463fee8??0xffeee8(16772840)
17e40000??17e41000??18e3fff8??0xffeff8(16773112)
...
5fee0000??5fee1000??603b86e4??0x4d76e4(5076708)
Large?object?heap?starts?at?0x03d81000segment?????begin??allocated??????size
03d80000??03d81000??04cd0f70??0xf4ff70(16056176)
3e8f0000??3e8f1000??3f88bd80??0xf9ad80(16362880)
3f8f0000??3f8f1000??4075eeb0??0xe6deb0(15130288)
Total?Size:??????????????Size:?0x44c33258?(1153643096)?bytes.
------------------------------
GC?Heap?Size:????Size:?0x44c33258?(1153643096)?bytes.

從卦中的 GC Heap Size= 1.15G 來看,原來都是被 GCHeap 給弄沒了,它吃掉了這么多內存是正常還是異常現象呢?這個還是取決于程序的業務邏輯,比如人家有一個小緩存什么的。

3. 真的要讓程序背鍋嗎

既然分析到這里,含著淚也得分析下去,可以使用 !dumpheap -stat 看下托管堆使用。

0:000>?!dumpheap?-stat
Statistics:MT????Count????TotalSize?Class?Name
71430958??1420366?????66249092?System.Int32[]
...
174f4300??6852848????109645568?xxx.Mes.xxxPlatInfo
174f4194??6852848????164468352?xxx.Mes.Platxxx
7142eb40??6923571????210298518?System.String
00dbb9a0??1788917????434963034??????Free

如果你有足夠的分析經驗,一看就能看出問題,比如 xxx.Mes.xxxPlatInfoxxx.Mes.Platxxx 對象高達 685w ,對象之間的排列布局很容易造成大量 Free 塊,也叫做堆碎片化,真的很難看,類似下面這樣。

0:000>?!dumpheap?5cee1000??5dede324Address???????MT?????Size...
5cee17c8?00dbb9a0???????38?Free
5cee17f0?174f4194???????24?????
5cee1808?7142eb40???????30?????
5cee1828?174f4300???????16?????
5cee1838?00dbb9a0???????10?Free
5cee1844?174f4194???????24?????
5cee185c?7142eb40???????30?????
5cee187c?174f4300???????16?????
5cee188c?00dbb9a0???????46?Free
5cee18bc?174f4194???????24?????
5cee18d4?7142eb40???????30?????
5cee18f4?174f4300???????16?????
5cee1904?00dbb9a0???????10?Free
5cee1910?174f4194???????24?????
5cee1928?7142eb40???????30?????
5cee1948?174f4300???????16?????
5cee1958?00dbb9a0???????50?Free
5cee198c?174f4194???????24?????
5cee19a4?7142eb40???????30?????
5cee19c4?174f4300???????16?????
5cee19d4?00dbb9a0??????130?Free
...

接下來在 xxx.Mes.xxxPlatInfo 中抽一個對象觀察它的引用根,為什么沒有被 GC 回收。

a25792e38a044b31a0e1ece6acdb4ac5.png

從圖中可以看到,它被 xxxPlatInfo 類下的 ConcurrentDictionary 中的 List 持有,我翻看了幾個,Size 都比較大,比如下面輸出:

0:000>?!do?0ea19634
Name:????????System.Collections.Generic.List`1[[xxx]]
MethodTable:?174f4350
EEClass:?????71006b4c
Size:????????24(0x18)?bytes
Fields:MT????Field???Offset?????????????????Type?VT?????Attr????Value?Name
7143e0fc??400188f????????4?????System.__Canon[]??0?instance?04bd0f60?_items
71430994??4001890????????c?????????System.Int32??1?instance???200367?_size
71430994??4001891???????10?????????System.Int32??1?instance???200367?_version
7142eee0??4001892????????8????????System.Object??0?instance?00000000?_syncRoot
7143e0fc??4001893????????4?????System.__Canon[]??0???static??<no?information>0:000>?!DumpObj?/d?0e6b9888
Name:???????System.Collections.Generic.List`1[[xxx]]
MethodTable:?174f4350
EEClass:?????71006b4c
Size:????????24(0x18)?bytes
Fields:MT????Field???Offset?????????????????Type?VT?????Attr????Value?Name
7143e0fc??400188f????????4?????System.__Canon[]??0?instance?3f78bd70?_items
71430994??4001890????????c?????????System.Int32??1?instance???171806?_size
71430994??4001891???????10?????????System.Int32??1?instance???171806?_version
7142eee0??4001892????????8????????System.Object??0?instance?00000000?_syncRoot
7143e0fc??4001893????????4?????System.__Canon[]??0???static??<no?information>

將這些信息反饋給朋友后,朋友說 List 這么多是有問題的,排查之后是 List 在多線程情況下有問題,修正之后問題得到解決。

三:總結

這次事故主要是由于朋友在處理線程安全集合 ConcurrentDictionary<xxx, List<xxx>> 的過程中,對其中的 List<xxx> 沒有合理的線程安全處理,導致數據的異常暴增,最終把緊張的 2G 虛擬地址用盡。

教訓就是:key 線程安全了, value 也要記的安全哦!

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

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

相關文章

HTTPS協議開通,Apache服務器CSR簽名申請

登錄您的服務器終端 (SSH)。在命令提示符下&#xff0c;鍵入以下命令&#xff1a;openssl req -new -newkey rsa:2048 -nodes -keyout yourdomain.key -out yourdomain.csr將 yourdomain 替換為您要保護的域名。例如&#xff0c;如果您的域名是 coolexample.com&#xff0c;您就…

首次公開!單日600PB的計算力--阿里巴巴EB級大數據平臺的進擊

摘要&#xff1a; 每年的雙11之前&#xff0c;也是MaxCompute各種乾坤大挪移落定的時候&#xff0c;因為雙11就是各種大折騰項目的自然deadline。在今年雙11之前&#xff0c;一路向北遷移和在離線混部項目&#xff0c;將杭州集群除螞蟻外整體遷移到張北&#xff0c;涉及了絕大部…

軟件測試金字塔

軟件測試金字塔 在敏捷方法中&#xff0c;持續集成是其基石&#xff0c;持續集成的核心是自動化測試。下面這篇關于測試金字塔的文章&#xff0c;來自大師Martin Fowler。 測試金字塔的概念來自Mike Cohn&#xff0c;在他的書Succeeding With Agile中有詳細描述&#xff1a;測試…

使用pm2守護你的.NET Core應用程序

簡介PM2是常用的node進程管理工具&#xff0c;它可以提供node.js應用管理&#xff0c;如自動重載、性能監控、負載均衡等。同類工具有Supervisor、Forever等。pm2是一個進程管理工具,可以用它來管理你的node進程&#xff0c;并查看node進程的狀態&#xff0c;當然也支持性能監控…

C-指針02 2017/11/24

/* 復習 1.指針類型 int *指針類型 指針指向的變量類型指針指向哪個變量2.基本數據類型 4種指針類型 存放的地址 和系統有關系 4個字節數組類型結構體 枚舉 聯合3.指針加法減法 p 和數組搭配使用4.兩個運算符 *取值(解引用) &取地址5. *(pi) p[i] …

程序員搞笑段子

轉載于:https://www.cnblogs.com/Zhusi/p/10083474.html

學習之旅——工作記錄日志2017.7.09

1.例子&#xff1a;在dev_lala上開發完畢后&#xff0c;切換到dev分支&#xff0c;在此分支上pull最新的代碼來保證dev上的代碼是最新的。在dev分支上git branch -b haha一個新的分支haha&#xff0c; 用git log dev_lala查看提交記錄&#xff0c;將我自己的幾個記錄加到haha分…

Git常用命令與基本操作

Git操作指令系統配置基本命令獲取/刪除Git倉庫更新記錄撤銷操作遠程倉庫的使用分支系統系統配置 git config 為系統自帶的配置指令&#xff0c;它可以控制GIT的行為和外觀 配置用戶信息 git config --global user.name "John Doe" git config --global user.email …

CA周記 - 在 Azure ML 上用 .NET 跑機器學習

.NET 是一個跨平臺&#xff0c;全場景應用的開源技術。你有在用 .NET 做機器學習/深度學習的應用嗎&#xff1f;如果從框架角度&#xff0c;ML.NET / Tensorflow.NET / 不斷在進步的 TorchSharp 通過幾年的發展已經開始穩定&#xff0c;但如果在一些大型項目上&#xff0c;特別…

iOS10 優化APP首次安裝網絡權限提示方案

我剛經歷了一場末日&#xff08;停電&#xff09;&#xff0c;特別是在你想寫文檔的時候。。。 言歸正傳&#xff0c;今天的問題是解決iOS10系統下首次按鈕APP彈出的網絡權限提示所帶來了問題以及優化。 起因 查了相關文章知道由于大陸工信部出臺的新規指出&#xff0c;應用在未…

su命令

從一個用戶切換到另一個用戶&#xff1a;su - ceshi(ceshi是用戶名) 查看當前用戶&#xff1a;whoami 在不切換用戶的情況執行另一個用戶的命令&#xff1a;例&#xff1a;su - -c "touch /tmp/111.txt" admin 若用戶沒有加目錄需要添加家目錄&#xff0c;并更改所有…

C語言基礎知識【數據類型】

C 數據類型1.在 C 語言中&#xff0c;數據類型指的是用于聲明不同類型的變量或函數的一個廣泛的系統。變量的類型決定了變量存儲占用的空間&#xff0c;以及如何解釋存儲的位模式。2.C 中的類型可分為以下幾種&#xff1a;序號 類型與描述1 基本類型&#xff1a;它們是算…

PS批量替換內容

在制作圖片物料的時候&#xff0c;有時會碰到需要制作大量內容格式一致&#xff0c;但部分文字或圖片不同的圖片&#xff0c;這里我們使用PS的變量功能 物料準備&#xff1a;準備好需要替換的圖片和文字&#xff0c;使用excel制作出需要替換的內容&#xff0c;第一行name和pic…

在 .NET 中執行 JavaScript 代碼

你好&#xff0c;這里是 Dotnet 工具箱&#xff0c;定期分享 Dotnet 有趣&#xff0c;實用的工具和組件&#xff0c;希望對您有用&#xff01;Jint 簡介如果您想在您的 .NET 程序中使用 Javascript&#xff0c;那么我推薦您使用 Jint。Jint 是適用于 .NET 的 開源 Javascript 解…

【本周面試題】第5周 - 開發工具相關

待整理轉載于:https://www.cnblogs.com/padding1015/p/10095424.html

JS 返回上一步(退回上一步上一個網頁)

鏈接式&#xff1a; <a href"javascript:history.go(-1)">返回上一步</a> <a href"<%Request.ServerVariables("HTTP_REFERER")%>">返回上一步</a> 按鈕式&#xff1a; <INPUT name"pclog" type&quo…

jmeter多用戶登錄跨線程組操作傳值

項目需求&#xff1a; 需要登錄兩個用戶A、B&#xff0c;用戶A操作完后會通知B&#xff0c;然后B再操作&#xff0c;B操作完結束或者再通知A。 實現思路&#xff1a; 1. 設置兩個線程組Ⅰ、Ⅱ&#xff0c;組Ⅰ添加cookie管理器&#xff0c;里面添加用戶A的操作&#xff1b;組Ⅱ…

手動修改美化7zip圖標 - 附替換文件

手動修改7zip圖標理論知識制作ico圖標文件替換資源重建圖標緩存效果圖替換文件7-Zip的自帶圖標是公認的丑&#xff0c;所以網上有很多7-Zip的圖標美化工具&#xff0c;用的最多的就是 7-Zip Theme Manager&#xff0c;自帶很多主題&#xff0c;但是最近我再使用7zTM修改圖標是卻…

.NET性能優化-復用StringBuilder

在之前的文章中&#xff0c;我們介紹了 dotnet 在字符串拼接時可以使用的一些性能優化技巧。比如&#xff1a;為StringBuilder設置 Buffer 初始大小使用ValueStringBuilder等等 不過這些都多多少少有一些局限性&#xff0c;比如StringBuilder還是會存在new StringBuilder()這樣…

如何使用vue組件搭建網頁并打包發布

vue組件化項目搭建及編譯打包發布引言開發環境開發環境介紹開發環境安裝使用模板創建項目編譯及打包發布引言 最近開始學習Vue&#xff0c;Vue 是一個前端框架&#xff0c;特點是數據綁定和組件化。網上很多教程&#xff0c;數據綁定和組件學習起來也都不困難&#xff0c;但是…