一:背景

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 00007ffe094baf50Address               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> ,address:000001c3d5c1bdf0</string,ditionary

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] 000001c3d8de7d38Name:        System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.DateTime, mscorlib]], mscorlib]]MethodTable: 00007ffdab940520EEClass:     00007ffe08e92920Size:        40(0x28) bytesFile:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dllFields:MT    Field   Offset                 Type VT     Attr            Value Name00007ffe094e9288  4003474       10             System.Int32      1     instance             58671583     hashCode00007ffe094e9288  4003475       14             System.Int32      1     instance                   -1     next00007ffe094ebf10  4003476        0           System.__Canon      0     instance     000001c2cec43610     key00007ffe094ebf10  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
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),毕竟这两个集合虚占内存太可怕了,下一篇我们来分析一下他们的扩容机制。

记一次排查线上程序内存的忽高忽低,又是大集合惹祸了相关推荐

  1. 内存地址 哪个程序_记一次排查线上程序内存的忽高忽低,又是大集合惹祸了...

    一:背景 1. 讲故事 昨天继续还技术债,优化一轮后的程序拉到线上后内存继续忽高忽低,低的时候20G,高的时候30G,过了一会又下降了几个G,毫无疑问,程序中有什么集合或者什么操作占用了大量内存,所以 ...

  2. 记一次排查线上full gc过程

    序 最近频繁收到线上报警,就看看到底啥原因 二 导出dump文件 2.1 查找报警对应的进程 ps -ef|grep XX 是23898,看一下gc情况: 这才不到半小时,fgc就增加了好几次. jm ...

  3. 一次排查线上线程池数量过高的报警经历

    线上jstack查看正常机器和问题机器对比堆栈信息,发现大量的http-nio-1601-exec-线程在等待,查看线上监控发现优惠券接口调用量飙升,中午商家做活动抢券,根据线程名字可以看出是tomc ...

  4. 原创|面试官:线上服务器CPU占用率高如何排查定位问题?

    国外开发者平台 HankerRank 发布的 2018 年开发者技能调查报告中有一项关于"雇主最看重哪些核心能力"的调查,结果显示如下: 排名前几的比较受重视的能力分别为:解决问题 ...

  5. 服务器定位cpu高占用率代码php,面试官:线上服务器CPU占用率高如何排查定位问题?,...

    面试官:线上服务器CPU占用率高如何排查定位问题?, 国外开发者平台 HankerRank 发布的 2018 年开发者技能调查报告中有一项关于"雇主最看重哪些核心能力"的调查,结果 ...

  6. 面试官:线上服务器CPU占用率高如何排查定位问题?

    开发十年,就只剩下这套架构体系了!! 国外开发者平台 HankerRank 发布的 2018 年开发者技能调查报告中有一项关于"雇主最看重哪些核心能力"的调查,结果显示如下:  ...

  7. 线上服务器CPU占用率高如何排查定位问题?

    (关联着看看这篇文章:https://blog.csdn.net/u011277123/article/details/103768939) 解决问题的能力以超高比例排名第一,这也是为什么很多面试过程 ...

  8. 服务器cpu占用率高怎么解决,线上服务器CPU占用率高怎么办?

    如果线上服务器发生CPU占用率高时,应该如何排查并定位问题. 1.问题发现 本文整理自一个真实的案例,是楼主负责的业务,在一次大促之前的压测时发现了这个问题. 在每次大促之前,我们的测试人员都会对网站 ...

  9. 线上服务器内存分析及问题排查

    转载自  线上服务器内存分析及问题排查 平常的工作中,在衡量服务器的性能时,经常会涉及到几个指标,load.cpu.mem.qps.rt等.每个指标都有其独特的意义,很多时候在线上出现问题时,往往会伴 ...

最新文章

  1. 关于CPU指标的解释
  2. LiveVideoStack成立5周年生日快乐!一路走来,感谢有你!
  3. 08.update_by_query操作
  4. php mongodb 连接失败,解决PHP使用普通账号连接mongodb报错问题
  5. Datalore:用于机器学习可视化的新Web方案!
  6. 通过指针便利图像元素
  7. MySQL中函数CONCAT及GROUP_CONCAT 对应oracle中的wm_concat
  8. 关于数据库备份的问题
  9. java自定义日志_Java 自定义日志写入
  10. Android View事件机制 21问21答
  11. mysql seconds_behind_master_MySQL同步状态双Yes的假象及seconds_behind_master的含义
  12. CSMA/CA与CSMA/CD
  13. TCP编程、UDP编程
  14. 东北大学c语言作业答案,{东北大学}2018年秋学期《画法几何及土木建筑制图》在线作业2课后参考答案...
  15. S3存储服务间数据同步工具Rclone介绍
  16. UnicodeDecodeError: 'gbk' codec can't decode byte 0xfe in position 575056: illegal multibyte sequenc
  17. 捋一捋DSP里的重叠保留法
  18. 根据经纬度查询两点距离(sql)
  19. Android 关于佳博和汉印蓝牙热敏打印机开发,kotlin爬虫app
  20. java哥验证德巴赫猜想,C语言验证哥德巴赫猜想

热门文章

  1. poj 1860 Currency Exchange (最短路bellman_ford思想找正权环 最长路)
  2. [导入]Asp.net中动态在中加入Scrpit标签
  3. Unity中Time.deltaTime的含义及其应用
  4. 如何开发Teams Bot
  5. Android添加item动画,RecyclerView基础篇-Item添加动画
  6. 学go语言能做什么工作?
  7. bugly中批量隐藏版本
  8. 如何节约数据中心空间
  9. 设置 Xcode 自动生成代码片段
  10. Java Socket实战之四 传输压缩对象