一:背景

1. 讲故事

前天写了一篇大内存排查在园子里挺火,这是做自媒体最开心的事拉,干脆再来一篇满足大家胃口,上个月我写了一篇博客提到过使用bitmap对原来的List<CustomerID>进行高强度压缩,将原来的List内存压缩了将近106倍,但是bitmap不是一味的好,你必须在正确的场景中使用,而不是闭着眼睛滥用,bitmap在C#中对应的集合是BitArray。

好像剧透了????????????,结果就是BitArray的滥用导致内存小10G的涨跌,不过这种东西重点还是看解决思路,写给以后的自己,可不能让这难得的实践经验蒸发啦~~~

二:解决思路

1. 一看托管堆

看托管堆虽然是一个好主意,但也不是每次都凑效,毕竟造成内存暴涨暴跌的原因各种各样,就像人感冒有风寒,风热和病毒性,对吧????,还是使用老命令: !dumpheap -stat -min 102400 ,在托管堆上找大于100M的对象。


0:030> !dumpheap -stat -min 102400
Statistics:MT    Count    TotalSize Class Name
00007ffe094ec988        1      1438413 System.Byte[]
00007ffdab934c48        1      1810368 System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.Collections.Generic.HashSet`1[[System.Int64, mscorlib]], System.Core]][]
00007ffe094e6948        1      2527996 System.String
00007ffdab9ace78        4     29499552 System.Collections.Generic.Dictionary`2+Entry[[System.Int64, mscorlib],[System.DateTime, mscorlib]][]
00007ffe094e4078        4    267342240 System.String[]
00007ffe094e9220      135    452683336 System.Int32[]
00007ffdab8cd620      123   1207931808 System.Collections.Generic.HashSet`1+Slot[[System.Int64, mscorlib]][]
00007ffe094c8510      185   1579292760 System.Int64[]
00007ffdab9516b0      154   1934622720 System.Linq.Set`1+Slot[[System.Int64, mscorlib]][]
000001cc882de970      347   3660623866      Free
Total 1371 objects

去掉一些敏感类后,再观察好像没有特别显眼的集合,像 System.Int64[] ,System.Linq.Set1+Slot[[System.Int64, mscorlib]][]一般都是用作其他集合的内存存储,很多时候用!gcroort 抓不出来,最大的反而是Free列,有347个碎片,高达 3.5G,说明此时的大对象堆是一塌糊涂啊,要是GC能帮忙压缩一下该多好????。

2. 查看每一个线程的调用栈

先惯性的偷窥一下程序中有多少个线程。


0:000> !threads
ThreadCount:      74
UnstartedThread:  0
BackgroundThread: 72
PendingThread:    0
DeadThread:       0
Hosted Runtime:   noLock  ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception0    1 2958 000001cc882e5a40    2a020 Preemptive  0000000000000000:0000000000000000 000001cc882d8db0 1     MTA2    2 2358 000001cc883122c0    2b220 Preemptive  000001D41B132930:000001D41B1348A0 000001cc882d8db0 0     MTA (Finalizer)3    4 2204 000001cc883ae5d0  102a220 Preemptive  0000000000000000:0000000000000000 000001cc882d8db0 0     MTA (Threadpool Worker)5    7 278c 000001cca29d8ef0  202b220 Preemptive  000001D41AB53A98:000001D41AB55A58 000001cc882d8db0 1     MTA6   40 2a64 000001cca3048f10  1020220 Preemptive  0000000000000000:0000000000000000 000001cc882d8db0 0     Ukn (Threadpool Worker)7   46  e34 000001cca311c390  202b220 Preemptive  0000000000000000:0000000000000000 000001cc882d8db0 0     MTA8   47 27d8 000001cca3115e00    2b220 Preemptive  0000000000000000:0000000000000000 000001cc882d8db0 0     MTA...

可以看到当前有74个线程,后台线程有72个,接下来用 ~*e !clrstack 查看每个托管线程都在做什么,由于内容太多,我就节选一下了哈。

0:000> ~*e !clrstack
OS Thread Id: 0x2d64 (29)Child SP               IP Call Site
000000d908cfe698 00007ffe28646bf4 [GCFrame: 000000d908cfe698]
000000d908cfe768 00007ffe28646bf4 [HelperMethodFrame_1OBJ: 000000d908cfe768] System.Threading.Monitor.ObjWait(Boolean, Int32, System.Object)OS Thread Id: 0x214c (30)Child SP               IP Call Site
000000d90957e6e8 00007ffe28646bf4 [GCFrame: 000000d90957e6e8]
000000d90957e7b8 00007ffe28646bf4 [HelperMethodFrame_1OBJ: 000000d90957e7b8] System.Threading.Monitor.ObjWait(Boolean, Int32, System.Object)OS Thread Id: 0x1dc0 (40)Child SP               IP Call Site
000000d950ebe878 00007ffe28646bf4 [GCFrame: 000000d950ebe878]
000000d950ebe948 00007ffe28646bf4 [HelperMethodFrame_1OBJ: 000000d950ebe948] System.Threading.Monitor.ObjWait(Boolean, Int32, System.Object)OS Thread Id: 0x274c (53)Child SP               IP Call Site
000000d9693fe518 00007ffe28646bf4 [GCFrame: 000000d9693fe518]
000000d9693fe5e8 00007ffe28646bf4 [HelperMethodFrame_1OBJ: 000000d9693fe5e8] System.Threading.Monitor.ObjWait(Boolean, Int32, System.Object)
000000d9693fe700 00007ffe09314d05 System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken)
000000d9693fe790 00007ffe0930d996 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken)
000000d9693fe800 00007ffe09c9b7a1 System.Threading.Tasks.Task.InternalWait(Int32, System.Threading.CancellationToken)

发现一个奇怪的现象,有4个线程 29,30,40,53 在 Monitor.ObjWait 处卡住了,从调用栈来看这四个家伙正在准备向Mongodb批量插入数据[InsertBatch],此时应该有其他的一个线程先行获取到了lock正在做InsertBatch,这四个线程在等待,如何觉得抽象了,我画一张图:

3. 寻找insertbatch处的集合

这里我就拿 30号线程说事,从上图调用栈中你应该看到一个System.Collections.Generic.IEnumerable1<System.__Canon>,从IEnumerable中可以猜测实现类应该是List或者HashSet这样的集合,接下来用 !dso 把30号线程栈上的对象全部dump出来。

从图中看应该就是这个 List<xxx.Common.GroupConditionCustomerIDCacheModel>,然后用!objsize ➕!do 给List量个尺寸并且dump一下。


0:030> !objsize 000001d3fa581518 sizeof(000001d3fa581518) = 1487587080 (0x58aac708) bytes (System.Collections.Generic.List`1[[DataMipCRM.Common.GroupConditionCustomerIDCacheModel, DataMipCRM.Common]])
0:030> !do 000001d3fa581518
Name:        System.Collections.Generic.List`1[[DataMipCRM.Common.GroupConditionCustomerIDCacheModel, DataMipCRM.Common]]
MethodTable: 00007ffdab9557d0
EEClass:     00007ffe08eb22a0
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
00007ffe09478740  4001871        8     System.__Canon[]  0 instance 000001d3fa5b9bf8 _items
00007ffe094e9288  4001872       18         System.Int32  1 instance             1520 _size
00007ffe094e9288  4001873       1c         System.Int32  1 instance             1520 _version
00007ffe094e6f28  4001874       10        System.Object  0 instance 0000000000000000 _syncRoot
00007ffe09478740  4001875        8     System.__Canon[]  0   static  <no information>

可以看出list占用 1487587080/1024/1024=1.4G,尼玛这么大,吓人哈,从_size看也就1520个,说明重点都在 _items 数组里啦,接下里用 da 把第一个item拿出来解剖下。


0:030> !da -length 1 -details 000001d3fa5b9bf8
Name:        DataMipCRM.Common.GroupConditionCustomerIDCacheModel[]
MethodTable: 00007ffdab955e10
EEClass:     00007ffe08eaaa00
Size:        16408(0x4018) bytes
Array:       Rank 1, Number of elements 2048, Type CLASS
Element Methodtable: 00007ffdab955740
[0] 000001d3fa581540Name:        DataMipCRM.Common.GroupConditionCustomerIDCacheModelMethodTable: 00007ffdab955740EEClass:     00007ffdab94b9e8Size:        64(0x40) bytesFile:        D:\LuneceService\DataMipCRM.Common.dllFields:MT    Field   Offset                 Type VT     Attr            Value Name00007ffdaac69258  4000589       28     ...oDB.Bson.ObjectId      1     instance     000001d3fa581568     <_id>k__BackingField00007ffe094e9288  400058a       20             System.Int32      1     instance                 1901     <ShopId>k__BackingField00007ffe094e6948  400058b        8            System.String      0     instance     000001d3f7154070     <GroupConditionHasCode>k__BackingField00007ffe094e6948  400058c       10            System.String      0     instance     000001cca7b46ac0     <unit>k__BackingField00007ffe094f1cb0  400058d       18     ...lections.BitArray      0     instance     000001d3fa581580     <customeridArray>k__BackingField

从最后一行的Type列可以看到有一个 BitArray 类,还是一样,先量个尺寸再打出来。


0:030> !objsize 000001d3fa581580     sizeof(000001d3fa581580) = 956008 (0xe9668) bytes (System.Collections.BitArray)
0:030> !do 000001d3fa581580
Name:        System.Collections.BitArray
MethodTable: 00007ffe094f1cb0
EEClass:     00007ffe08ead968
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
00007ffe094e9220  40017e2        8       System.Int32[]  0 instance 000001d5320c6d18 m_array
00007ffe094e9288  40017e3       18         System.Int32  1 instance          7647524 m_length
00007ffe094e9288  40017e4       1c         System.Int32  1 instance                2 _version
00007ffe094e6f28  40017e5       10        System.Object  0 instance 0000000000000000 _syncRoot

从output中看,这个bitarray占用 956008/1024/1024 = 0.91M,真tmd的大,看bit位有 764w 个,说明有一个CustomerID=7647524-1放在这里面,问题基本上就算找到了,现在终于知道内存为什么这么大,算一下就出来了。

四:总结

最后去问了下搬砖的为什么这么写,是因为给客户展示的一张报表,为了加速。。。将每一个点上的人群保存起来放在BitArray上然后进入Monogdb缓存,这样客户后续选择某一个点进行 下钻 操作的话,可以直接从mongodb中将BitArray的人群复原出来,免去程序重复计算之苦,因为每个点的人群具有排他性,落在每个点上的人可能只有几十,几百,几千,而偏偏这家客户有800w之多,自然这个CustomerID就特别大了,更不巧的就用了bitArray存少量的大数字,这些赶在一起之后,就是一个典型的滥用bitarray,您说的?

BitArray虽好,但请不要滥用,又一次线上内存暴增排查相关推荐

  1. 滥用线程,导致线上线程池被撑爆的一次意外

    事故的背景 简单的描述一下业务场景,项目里面有一个分布式定时job ,定期去扒取数据,那么有两层循环,第一层是大概1000条数据 ,然后第二层循环每个数据下面大概20个子数据,然后通过多线程的方式去扒 ...

  2. 线上redis一般安在linux_redis的zset有多牛?请把耳朵递过来

    原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处. 本篇文章很短,但信息量很大,是关于redis的zset.我来分享一点遇到过的线上数据,或许对你的决策有帮助. redis支 ...

  3. 各地延迟上课,请收好这份线上教学操作指南

    为控制疫情继续扩散,各地教育部门与院校积极响应教育部<关于2020年春季学期延期开学的通知>,纷纷推迟开学时间.不少学校开启了线上教学通道,QQ群实现线上连麦早读.在线授课.语音互动.作业 ...

  4. 使用thinkPhp,修改线上数据库的配置,请删除 Runtime 中的所有内容后重试

    2019独角兽企业重金招聘Python工程师标准>>> 使用thinkPhp,修改线上数据库的配置,请删除 Runtime 中的所有内容后重试 转载于:https://my.osch ...

  5. 微信:签名不对,请检查签名是否与开放平台上填写的一致

    今天接完微信登录SDK后,满怀期待打开app,提示"微信:签名不对,请检查签名是否与开放平台上填写的一致",给我泼了一下冷水,还好这冷水不是特别冷!!! 1.用一个工具,名字叫Ge ...

  6. 彻底解决问题:签名不对,请检查签名是否与开放平台上填写的一致

    彻底解决问题:签名不对,请检查签名是否与开放平台上填写的一致 背景 问题分析 思路分析: 应用签名和应用包名一致,仍报错的解决办法: 思路分析: Android APP数字证书和应用签名的用途和关系 ...

  7. Android_微信分享时提示“签名不对,请检查签名是否与开放平台上填写的一致。”

    传送门: Android_新浪微博分享提示"21338:sso package or sign error": https://blog.csdn.net/pang78755961 ...

  8. 签名不对,请检查签名是否与开发平台上填写的一致

    在使用微信登录的时候提示 签名不对,请检查签名是否与开发平台上填写的一致,从以下几个方面去定位问题: 确保 目标应用apk 是 release 包: 检查 AppID.AppSecret 和目标应用的 ...

  9. 共有 n 个非凡拔尖的人才参加竞选,现在票数已经统计完毕,得票最多的人当选。请你算出谁能够当上学生会主席。

    题目描述 地球历公元 2021 年,中国石油大学(华东)学生会主席竞选,共有 n 个非凡拔尖的人才参加竞选,现在票数已经统计完毕,得票最多的人当选.请你算出谁能够当上学生会主席. 题目保证所有人的票数 ...

最新文章

  1. 【Sql Server】Database-sql语言的流程控制语句
  2. SLF4J 的几种实际应用模式--之二:SLF4J+Logback
  3. Flash数据的采集方法-搜房房价走势采集
  4. 马斯克采访:要么死的安然,要么活得绚烂
  5. [CODEVS1258]关路灯
  6. Tomcat6.0 中数据源的配置
  7. nagios搭建(一):nagios3.2的搭建
  8. Java开发Web Service的几种解决方案
  9. 当程序员真难,又一次被编辑器坑了!
  10. python游戏编程快速上手豆瓣_Python游戏编程快速上手(第4版)
  11. python实现图片自动轮番播放
  12. 【BZOJ1226】学校食堂(动态规划,状态压缩)
  13. 游戏筑基开发之动态数组(C语言)
  14. 中国省,市,区 json数据
  15. 基于优化LSTM 模型的股票预测
  16. win10彻底禁用自动更新,win10怎样彻底关闭自动更新,永久关闭win10自动更新,win10更新助手...
  17. 常见的网站攻击以及如何防御自己的网站被恶意攻击
  18. JVM学习四:垃圾收集器与内存回收策略
  19. python之excel处理画图
  20. 1500 多款 3D Emoji,承担你的表情包!

热门文章

  1. 关于jstl.jar引用问题及解决方法
  2. [Usaco2007 Demo][BZOJ1628] City skyline
  3. 如何查看自己的ubutu系统是32位的还是64位的
  4. 二 ASP.NET AutoPostBack
  5. 常用MIME类型(Flv,Mp4的mime类型设置)
  6. 项目中的模块剥离成项目_使用MCEBuddy 2从电视录制中剥离广告
  7. acm教程 java版_[转]ACM之java速成
  8. mysql两种引擎的适用场景_MySQL两种引擎的区别和应用场景
  9. Java8基础之super关键字
  10. 基于spring boot 的ssm项目的简单配置