共计 7203 个字符,预计需要花费 19 分钟才能阅读完成。
本篇内容介绍了“Android 程序内存忽高忽低怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让丸趣 TV 小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
一:背景
1. 讲故事
昨天继续还技术债,优化一轮后的程序拉到线上后内存继续忽高忽低,低的时候 20G,高的时候 30G,过了一会又下降了几个 G,毫无疑问,程序中有什么集合或者什么操作占用了大量内存,所以准备在 28,29G 的时候抓 dump 分析分析。
二:解决思路
从快照中找问题就像看病一样,根据病象猜测,都有一套经验可循。
1. 把托管堆中 10M 的对象找出来
通常应对大集合从托管堆入手最简单,看哪个类型占用空间大,基本就是它出问题了,为了避免把所有类型都打出来, 这里设置一下过滤,把小于 10M 都踢掉,可以用 !dumpheap -stat -min 10240,把敏感对象脱敏掉。
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 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。
3. 寻找集合所在的代码块
字典占用 279M 我是知道了,但怎么知道这个字典是在哪一个代码块呢?要寻找答案也容易,通过!gcroot 找到它的引用根,通过引用链就可以找到它的代码区块,简直不要太实用
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 线程上
2 字典在 GetFlowAwayCustomer 方法中,大概可以看出是计算流失客户的。
3 调用链顶部是最大的集合 Dictionary string,Ditionary int,DateTime,address:000001c3d5c1bdf0
4. 寻找更多信息
1 挖字典内容
有了最大的字典,我们来看看最大字典 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 我就不输出了。
2 挖线程栈
有了字典内容,大家继续看一下此时这个线程 [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)
大家应该知道,有扩容就有虚占内存。
“Android 程序内存忽高忽低怎么解决”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注丸趣 TV 网站,丸趣 TV 小编将为大家输出更多高质量的实用文章!