1. 程式人生 > >記一次排查線上程式記憶體的忽高忽低,又是大集合惹禍了

記一次排查線上程式記憶體的忽高忽低,又是大集合惹禍了

## 一:背景 ### 1. 講故事 昨天繼續還技術債,優化一輪後的程式拉到線上後記憶體繼續忽高忽低,低的時候20G,高的時候30G,過了一會又下降了幾個G,毫無疑問,程式中有什麼集合或者什麼操作佔用了大量記憶體,所以準備在28,29G的時候抓dump分析分析。 ## 二:解決思路 從快照中找問題就像看病一樣,根據病象推測,都有一套經驗可循。 ### 1. 把託管堆中>10M的物件找出來 通常應對大集合從託管堆入手最簡單,看哪個型別佔用空間大,基本就是它出問題了,為了避免把所有型別都打出來,這裡設定一下過濾,把小於10M都踢掉, 可以用 `!dumpheap -stat -min 10240`,把敏感物件脫敏掉。 ``` C# 0:000> !dumpheap -stat -min 10240 Statistics: MT Count TotalSize Class Name 00007ffe094e6fc0 4 523776 System.Object[] 00007ffe094e6948 6 7179822 System.String 00007ffe0780da08 33 46514160 System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.Collections.Generic.HashSet`1[[System.Int32, mscorlib]], System.Core]][] 00007ffe09f95f40 250 188739344 System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.Int32, mscorlib]][] 00007ffe094ec988 18 540828823 System.Byte[] 00007ffe07802da8 1620 622578672 System.Linq.Set`1+Slot[[System.Int32, mscorlib]][] 000001bc0452e600 1389 1038494910 Free 00007ffe094baf50 68 1128274800 System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.DateTime, mscorlib]][] 00007ffe094e9220 2224 1513951832 System.Int32[] 00007ffe07819df8 2232 1668042480 System.Collections.Generic.HashSet`1+Slot[[System.Int32, mscorlib]][] 00007ffe094c8510 226 1672164568 System.Int64[] 00007ffdab8676e8 1137 1901228880 System.Collections.Generic.HashSet`1+Slot[[System.Int64, mscorlib]][] 00007ffdab89b3b0 136 1986723840 System.Linq.Set`1+Slot[[System.Int64, mscorlib]][] Total 13321 objects ``` ### 2. 找出堆中可疑的物件 因為程式啟動後作為記憶體資料庫,所以有包含指定類的大集合物件很正常,倒數第7行有一個`Dictionary` 佔用空間挺大的,`1128274800/1024/1024=1G`,這個貌似不是基礎資料,應該是中間變數,方法表地址為`00007ffe094baf50`, 通過它可以找到這68個集合的記憶體地址。 ``` C# 0:028> !dumpheap -mt 00007ffe094baf50 Address MT Size 000001c2f262a708 00007ffe094baf50 69438000 000001c1bb8e1020 00007ffe094baf50 16147872 000001c1bce04760 00007ffe094baf50 33486336 000001c37e8f1020 00007ffe094baf50 143987328 000001c44e8f1020 00007ffe094baf50 287974800 000001c3c419b268 00007ffe094baf50 16147872 000001c3f6b9ac28 00007ffe094baf50 16147872 000001c467336fa0 00007ffe094baf50 33486336 000001c46f3fa760 00007ffe094baf50 69438000 000001c489df3668 00007ffe094baf50 16147872 000001c494166828 00007ffe094baf50 33486336 000001c4a68f1020 00007ffe094baf50 69438000 000001c4d4c5c290 00007ffe094baf50 16147872 000001c4da8f1058 00007ffe094baf50 33486336 000001c4de8f1020 00007ffe094baf50 69438000 000001c5028f1058 00007ffe094baf50 33486336 000001c5068f1020 00007ffe094baf50 33486336 ... ``` 下一步挑幾個大的 `Dictionary` 看看,比如這一行: `000001c44e8f1020 00007ffe094baf50 287974800`,計算一下size:279M。 ### 3. 尋找集合所在的程式碼塊 字典佔用279M我是知道了,但怎麼知道這個字典是在哪一個程式碼塊呢? 要尋找答案也容易,通過`!gcroot` 找到它的引用根,通過引用鏈就可以找到它的程式碼區塊,簡直不要太實用,