昨天繼續還技術債,優化一輪後的程序拉到線上後內存繼續忽高忽低,低的時候20G,高的時候30G,過了一會又降低了幾個G,毫無疑問,程序中有什麼集合或者什麼操做佔用了大量內存,因此準備在28,29G的時候抓dump分析分析。數據庫
從快照中找問題就像看病同樣,根據病象猜想,都有一套經驗可循。優化
一般應對大集合從託管堆入手最簡單,看哪一個類型佔用空間大,基本就是它出問題了,爲了不把全部類型都打出來,這裏設置一下過濾,把小於10M都踢掉, 能夠用 !dumpheap -stat -min 10240
,把敏感對象脫敏掉。spa
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
由於程序啓動後做爲內存數據庫,因此有包含指定類的大集合對象很正常,倒數第7行有一個Dictionary<int,Datetime>
佔用空間挺大的,1128274800/1024/1024=1G
,這個貌似不是基礎數據,應該是中間變量,方法表地址爲00007ffe094baf50
, 經過它能夠找到這68個集合的內存地址。線程
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。3d
字典佔用279M我是知道了,但怎麼知道這個字典是在哪個代碼塊呢? 要尋找答案也容易,經過!gcroot
找到它的引用根,經過引用鏈就能夠找到它的代碼區塊,簡直不要太實用,😄😄😄。code
0:000> !gcroot 000001c4de8f1020 Thread 2da8: 00000017f4c7e5d0 00007ffdab758ca1 xxxx.xxxx.xxxx.GetFlowAwayCustomer(Int32, System.String, System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<xxxx>>) rbp-238: 00000017f4c7e628 -> 000001c3d5c1bdf0 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]], mscorlib]] -> 000001c3d8de7d10 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]], mscorlib]][] -> 000001c3d8d58630 System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]] -> 000001c4de8f1020 System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.DateTime, mscorlib]][]
從上面引用鏈能夠看到三點信息:對象
<1> 當前字典在 2da8 線程上blog
<2> 字典在 GetFlowAwayCustomer 方法中,大概能夠看出是計算流失客戶的。內存
<3> 調用鏈頂部是最大的集合 Dictionary<string,Ditionary<int,DateTime>> ,address:000001c3d5c1bdf0element
有了最大的字典,咱們來看看最大字典Dictionary<string,Ditionary<int,DateTime>>
佔用的內存大小。
0:000> !objsize 000001c3d5c1bdf0 sizeof(000001c3d5c1bdf0) = 340008256 (0x14441d40) bytes (System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]], mscorlib]])
根據sizeof(000001c3d5c1bdf0) = 340008256 (0x14441d40) bytes
計算一下:324M,尼瑪,這都是其中一個字典,難怪內存忽高忽低,如今你們確定特別想知道里面有啥東西,能夠用 da -> !do
去內部集合看一下。
0:000> !da -length 1 -start 1 -details 000001c3d8de7d10 Name: System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]], mscorlib]][] MethodTable: 00007ffdab940650 EEClass: 00007ffdab9405b8 Size: 192(0xc0) bytes Array: Rank 1, Number of elements 7, Type VALUETYPE Element Methodtable: 00007ffdab940520 [1] 000001c3d8de7d38 Name: System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]], mscorlib]] MethodTable: 00007ffdab940520 EEClass: 00007ffe08e92920 Size: 40(0x28) bytes File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 00007ffe094e9288 4003474 10 System.Int32 1 instance 58671583 hashCode 00007ffe094e9288 4003475 14 System.Int32 1 instance -1 next 00007ffe094ebf10 4003476 0 System.__Canon 0 instance 000001c2cec43610 key 00007ffe094ebf10 4003477 8 System.__Canon 0 instance 000001c3d7b45370 value 0:000> !do 000001c3d7b45370 Name: System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]] MethodTable: 00007ffe094b9ec8 EEClass: 00007ffe08e9d528 Size: 80(0x50) bytes File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 00007ffe094e9220 4001858 8 System.Int32[] 0 instance 000001c46e8f1020 buckets 00007ffe094baf50 4001859 10 ...ime, mscorlib]][] 0 instance 000001c46f3fa760 entries 00007ffe094e9288 400185a 38 System.Int32 1 instance 2512598 count 00007ffe094e9288 400185b 3c System.Int32 1 instance 3194430 version 00007ffe094e9288 400185c 40 System.Int32 1 instance -1 freeList 00007ffe094e9288 400185d 44 System.Int32 1 instance 0 freeCount 00007ffe094dabb8 400185e 18 ...Int32, mscorlib]] 0 instance 000001bc06272ab8 comparer 00007ffe0a0463e0 400185f 20 ...eTime, mscorlib]] 0 instance 0000000000000000 keys 00007ffe0a046258 4001860 28 ...eTime, mscorlib]] 0 instance 0000000000000000 values 00007ffe094e6f28 4001861 30 System.Object 0 instance 0000000000000000 _syncRoot
能夠看到大字典中7個元素,而後我挑了一個內嵌Dictionary,能夠看到這個內嵌字典的count=251w
,裏面的details我就不輸出了。
有了字典內容,你們繼續看一下此時這個線程 [2da8]
在作什麼?
0:028> ~~[2da8]s ntdll!NtWaitForSingleObject+0x14: 00007ffe`28646124 c3 ret 0:028> !clrstack OS Thread Id: 0x2da8 (28) Child SP IP Call Site 00000017f4c7e388 00007ffe28646124 [HelperMethodFrame: 00000017f4c7e388] 00000017f4c7e4f0 00007ffe09e48e52 System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]].Resize(Int32, Boolean) 00000017f4c7e560 00007ffe09316c65 System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]].Insert(Int32, System.DateTime, Boolean)
我靠,這個集合正在作擴容。。。你們應該知道,有擴容就有虛佔內存。
到這裏確定有人問,找出大集合了,解決方案是什麼? 由於是昨天才發現的,況且代碼不是我寫的,你問我哈??? 準備從兩方面入口, 業務邏輯上優化 ➕ 定製化集合(HashSet,Dictionary),畢竟這兩個集合虛佔內存太可怕了,下一篇咱們來分析一下他們的擴容機制。