如何在 .NET 程序万种死法中有效的生成 Dump (上)
一:背景
相信很多人都知道通過?任務管理器?抓取dump,雖然簡單粗暴,但無法滿足程序的無數種死法,比如:
內存膨脹,程序爆炸
CPU爆高,程序累死
應用無響應,用戶氣死
意外退出,和人生一樣
既然手工太弱雞,那有什么好的工具呢?除了 adplus,本文推薦一款神器?procdump, 下載地址:https://docs.microsoft.com/zh-cn/sysinternals/downloads/procdump ,還能支持 linux ????????????,具體怎么安裝就不細說了。
二:內存膨脹,程序爆炸
內存膨脹?這種情況我相信很有朋友都遇到過,我見過最多的案例就是用了小緩存 static,然后有意無意的忘記釋放,導致無限堆積終爆炸,那這種怎么用 procdump 去抓呢?
為了方便演示,我先寫一個無限分配內存的例子。
static?void?Main(string[]?args){List<string>?list?=?new?List<string>();for?(int?i?=?0;?i?<?int.MaxValue;?i++){list.Add(string.Join(",",?Enumerable.Range(0,?10000)));}Console.ReadLine();}將程序跑起來后,設置 procdump 在內存超過 1G 的時候自動抓取全內存 dump,使用如下命令.
C:\Windows\system32>procdump??ConsoleApp2?-m?1024?-ma?E:\net5\ConsoleApp1\ConsoleApp2\bin\DebugProcDump?v10.0?-?Sysinternals?process?dump?utility Copyright?(C)?2009-2020?Mark?Russinovich?and?Andrew?Richards Sysinternals?-?www.sysinternals.comProcess:???????????????ConsoleApp2.exe?(24112) Process?image:?????????E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug\ConsoleApp2.exe CPU?threshold:?????????n/a Performance?counter:???n/a Commit?threshold:??????>=?1024?MB Threshold?seconds:?????10 Hung?window?check:?????Disabled Log?debug?strings:?????Disabled Exception?monitor:?????Disabled Exception?filter:??????[Includes]*[Excludes] Terminate?monitor:?????Disabled Cloning?type:??????????Disabled Concurrent?limit:??????n/a Avoid?outage:??????????n/a Number?of?dumps:???????1 Dump?folder:???????????E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug\ Dump?filename/mask:????PROCESSNAME_YYMMDD_HHMMSS Queue?to?WER:??????????Disabled Kill?after?dump:???????DisabledPress?Ctrl-C?to?end?monitoring?without?terminating?the?process.[21:23:43]?Commit:????1087Mb [21:23:43]?Dump?1?initiated:?E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug\ConsoleApp2.exe_210323_212343.dmp [21:23:43]?Dump?1?writing:?Estimated?dump?file?size?is?1179?MB. [21:23:44]?Dump?1?complete:?1179?MB?written?in?1.3?seconds [21:23:44]?Dump?count?reached.從最后五行可以看出,當內存達到?1087M?的時候自動生成了 dump 文件,接下來用 windbg 看一看。
查看當前 process 的內存占用量,使用?!address -summary?即可
看到上面?PAGE_READWRITE?行的?(1.055 GB)?嗎?和剛才 Console 中的 1087M 遙相呼應,沒毛病。
尋找大對象,在托管堆中使用?!dumpheap -stat -min 1024?即可
從輸出的最后一行可以看出,System.String?有1w多個,接下來可以增加 ?-type?屬性篩選出?>10k?的字符串。
0:000>?!dumpheap?-type?System.String?-min?10240Address???????MT?????Size 03c75568?65d424e4????97792????? 03c8d378?65d424e4????97792???? 4a855060?65d424e4????97792?????Statistics:MT????Count????TotalSize?Class?Name 65d424e4????11452???1119913984?System.String Total?11452?objects0:000>?!gcroot?4a855060 Thread?36e4: ***?WARNING:?Unable?to?verify?checksum?for?ConsoleApp2.exe00b3f358?012108d1?ConsoleApp2.Program.Main(System.String[])?[E:\net5\ConsoleApp1\ConsoleApp2\Program.cs?@?18]ebp+18:?00b3f370->??02c71fd8?System.Collections.Generic.List`1[[System.String,?mscorlib]]->??02cce2ec?System.String[]->??4a855060?System.StringFound?1?unique?roots?(run?'!GCRoot?-all'?to?see?all?roots).從最后的?!gcroot?看,確實是被?Program.cs:18?行的 List 所持有,到此水落石出。
三:CPU爆高,程序累死
說起CPU爆高的案例,我發現更多的是在?非托管堆?上,比如GC回收,爭搶鎖等,很少有人能傻到在?托管層?上把cpu搞起來。
對了,分析CPU 爆高有一個小技巧,那就是連續抓 dump 快照,看兩個 dump 中的線程運行情況,這時候就非常適合 procdump,先來看測試代碼。
class?Program{static?void?Main(string[]?args){Parallel.For(0,?int.MaxValue,?(i)?=>{while?(true){}});Console.ReadLine();}}現在我設定?連續 5s 內 CPU 超過 70% 抓取 dump,直到 2 個為止?。
C:\Windows\system32>procdump??ConsoleApp2?-s?5?-n?2?-c?70?E:\net5\ConsoleApp1\ConsoleApp2\bin\DebugProcDump?v10.0?-?Sysinternals?process?dump?utility Copyright?(C)?2009-2020?Mark?Russinovich?and?Andrew?Richards Sysinternals?-?www.sysinternals.comProcess:???????????????ConsoleApp2.exe?(22152) Process?image:?????????E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug\ConsoleApp2.exe CPU?threshold:?????????>=?70%?of?system Performance?counter:???n/a Commit?threshold:??????n/a Threshold?seconds:?????5 Hung?window?check:?????Disabled Log?debug?strings:?????Disabled Exception?monitor:?????Disabled Exception?filter:??????[Includes]*[Excludes] Terminate?monitor:?????Disabled Cloning?type:??????????Disabled Concurrent?limit:??????n/a Avoid?outage:??????????n/a Number?of?dumps:???????2 Dump?folder:???????????E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug\ Dump?filename/mask:????PROCESSNAME_YYMMDD_HHMMSS Queue?to?WER:??????????Disabled Kill?after?dump:???????DisabledPress?Ctrl-C?to?end?monitoring?without?terminating?the?process.[22:25:47]?CPU:?95%?1s [22:25:48]?CPU:?100%?2s [22:25:50]?CPU:?96%?3s [22:25:51]?CPU:?98%?4s [22:25:52]?CPU:?99%?5s?(Trigger) [22:25:53]?Dump?1?initiated:?E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug\ConsoleApp2.exe_210323_222553.dmp [22:25:54]?Dump?1?complete:?5?MB?written?in?0.3?seconds [22:25:56]?CPU:?88%?1s [22:25:58]?CPU:?93%?2s [22:26:00]?CPU:?89%?3s [22:26:02]?CPU:?89%?4s [22:26:04]?CPU:?95%?5s?(Trigger) [22:26:05]?Dump?2?initiated:?E:\net5\ConsoleApp1\ConsoleApp2\bin\Debug\ConsoleApp2.exe_210323_222605.dmp [22:26:06]?Dump?2?complete:?5?MB?written?in?0.4?seconds [22:26:07]?Dump?count?reached.從最后輸出中可以看到,連續?5s?CPU 超過了 70% 抓取了 dump,總共來了2個。
現在 dump 有了,接下來用兩個 windbg 實例打開,驗證下 dump 的生成時間,如下圖所示:
從圖中可以看到,兩個 dump 生成時間相隔 12s,而且通過?!runaway?發現下面的線程:
14:2cb8
19:3f8c
...
都運行了長達 10s ,這說明什么?說明這二個線程應該在某個地方死循環了。。。對吧。。。
切到 14 號線程通過?!clrstack?看調用堆棧即可,都是死在?ConsoleApp2.Program+c.b__0_0(Int32)?這里出不來。。。
四:總結
感覺篇幅有點長了,就先說到這里吧,有興趣的話,可以把 procdump 拉下來玩一玩 ????。
END
工作中的你,是否已遇到 ...?
1. CPU爆高
2. 內存暴漲
3. 資源泄漏
4. 崩潰死鎖
5. 程序呆滯
等緊急事件,全公司都指望著你能解決...? 危難時刻才能展現你的技術價值,作為專注于.NET高級調試的技術博主,歡迎微信搜索: 一線碼農聊技術,免費協助你分析Dump文件,希望我能將你的踩坑經驗分享給更多的人。
總結
以上是生活随笔為你收集整理的如何在 .NET 程序万种死法中有效的生成 Dump (上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过Dapr实现一个简单的基于.net的
- 下一篇: 监控系统简介:使用 Prometheus