一:背景

1. 讲故事

七月底的时候有位朋友在wx上找到我,说他的程序内存占用8G,托管才占用1.5G,询问剩下的内存哪里去了?截图如下:

从求助内容看,这位朋友真的太客气了,动不动就谈钱,真伤感情,如果有朋友一直关注我的分享,应该知道我一直都是免费分析dump,当然我的知识和经验也是有边界的,有些dump我也搞不定,不过我还是尽自己最大努力去寻找答案。

在这里我有必要说一下职场,在我的潜意识或者在我的团队中,这些很难搞的问题当然由技术领导去搞定,但我发现有好几起却不是这样的,技术经理搞不定转包下来,下面搞不定就让他另请高明。。。????????????  有大佬可以分析下吗。

好了,闲话不多说,当务之急上windbg说话。

二:windbg 分析

1. 真的是非托管泄漏吗?

我在很多分析内存泄漏方面的文章都提到过,先要用二分法确定下是哪一部分的内存泄漏(托管还是非托管)。


0:000> !address -summary--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                    387     7df2`11ac1000 ( 125.946 TB)           98.39%
<unknown>                              2229      20c`a21bb000 (   2.049 TB)  99.75%    1.60%
Heap                                   1081        1`33914000 (   4.806 GB)   0.23%    0.00%
Image                                  1674        0`0e4be000 ( 228.742 MB)   0.01%    0.00%
Stack                                   973        0`0a140000 ( 161.250 MB)   0.01%    0.00%
TEB                                     324        0`00288000 (   2.531 MB)   0.00%    0.00%
Other                                    11        0`001d9000 (   1.848 MB)   0.00%    0.00%
PEB                                       1        0`00001000 (   4.000 kB)   0.00%    0.00%--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_MAPPED                              300      200`00f9e000 (   2.000 TB)  97.35%    1.56%
MEM_PRIVATE                            3869        d`dd7ed000 (  55.461 GB)   2.64%    0.04%
MEM_IMAGE                              2124        0`0fda4000 ( 253.641 MB)   0.01%    0.00%--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                387     7df2`11ac1000 ( 125.946 TB)           98.39%
MEM_RESERVE                            1763      20b`d9903000 (   2.046 TB)  99.60%    1.60%
MEM_COMMIT                             4530        2`14c2c000 (   8.324 GB)   0.40%    0.01%0:000> !eeheap -gc
Number of GC Heaps: 40
------------------------------
Heap Size:               Size: 0x3322e60 (53620320) bytes.
------------------------------
GC Heap Size:            Size: 0x603046b0 (1613776560) bytes.

!address -summary!eeheap -gc 两条命令看,确实如朋友所说:MEM_COMMIT=8.3G, GC Heap=1.5G, 我去,果然是难搞的非托管内存泄漏,既然是地狱模式,那就硬着头皮继续看吧,要想继续排查的话,首先得看 windows nt 堆。

2. 查看 windows nt堆

其实不管是托管的C#还是非托管的C,C++,它们分配内存最终都需要调用 Windows 的 VirtualAlloc,HeapAlloc API 到 windows nt 上,接下来的研究方向是如何查找这些 .net 看不到的 nt堆, 可以使用 windbg 的 !heap -s 命令。


0:000> !heap -s************************************************************************************************************************NT HEAP STATS BELOW
************************************************************************************************************************
LFH Key                   : 0x0e4dcfd61ab09dd9
Termination on corruption : ENABLEDHeap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast (k)     (k)    (k)     (k) length      blocks cont. heap
-------------------------------------------------------------------------------------
000001bacd190000 00000002 4841944 4810424 4840388  13556  2042   303    2    dd8   LFH
000001baccfb0000 00008000      64      4     64      2     1     1    0      0
000001bacd4d0000 00001002    8772   6748   7216   1045   191     4    0     38   LFHExternal fragmentation  15 % (191 free blocks)
000001bacdf90000 00001002    2636    404   1080     33     3     2    0      0   LFH
000001bace620000 00001002    8772   4052   7216   3874    13     7    0     1f   LFHExternal fragmentation  95 % (13 free blocks)
000001bace610000 00001003      60      8     60      6     1     1    0    N/A
000001bace540000 00001002    1616     24     60      4     2     1    0      1   LFH
000001baceb50000 00001002    4680   1228   3124    504    99     3    0      0   LFHExternal fragmentation  41 % (99 free blocks)
000001baceb20000 00041002      60      8     60      5     1     1    0      0
000001baceb10000 00041002    1616     68     60      4     3     1    0      0   LFH
000001c7738a0000 00001002   49336  19316  47780   8249    43    22    0    13b   LFHExternal fragmentation  42 % (43 free blocks)
000001c7753c0000 00001002   13712   8460  12156    968    29     6    0     1c   LFHExternal fragmentation  11 % (29 free blocks)
000001c7763f0000 00001002    8772   3944   7216    423    25     4    0     3f   LFH
000001ba977c0000 00001002    1080    376   1080    365     3     2    0      0
-------------------------------------------------------------------------------------

从上面的信息可以看出,当前有 14个 heap,其中最大的一个heap占了 4.8G,为啥这个heap这么大?接下来详细看下这个heap,可使用 !ext.heap -stat -h 000001bacd190000


0:000> !ext.heap -stat -h 000001bacd190000heap @ 000001bacd190000
group-by: TOTSIZE max-display: 20size     #blocks     total     ( %) (percent of total busy bytes)20034 8eee - 11df90858  (96.44)2ee0000 2 - 5dc0000  (1.98)851 1c2b - ea419b  (0.31)2ac00 28 - 6ae000  (0.14)27d8 268 - 5fdfc0  (0.13)24000 28 - 5a0000  (0.12)d51 564 - 47c8a4  (0.09)10d1 3e7 - 419f97  (0.09)fd1 415 - 409025  (0.09)29d1 12f - 317e5f  (0.07)138 18b0 - 1e1680  (0.04)12c 188b - 1cc2e4  (0.04)1000 17e - 17e000  (0.03)2000 8e - 11c000  (0.02)200 899 - 113200  (0.02)ad1 178 - fe2f8  (0.02)478 367 - f3448  (0.02)7c8 1b9 - d6788  (0.02)1c038 7 - c4188  (0.02)f520 c - b7d80  (0.02)

可能很多人看不懂上面的卦象,首先 busy表示那些最近分配还未释放的,从卦头看,size=20034 的 block 有 36590 个,总占用:11df90858 = 4797827160byte = 4.7G,接下来的疑问很显然了,这些 block 里面到底都是些什么???要想找到答案,把这 3w 多的 block 信息都显示出来,可以用命令:!ext.heap -flt s 20034


0:000> !ext.heap -flt s 20034_HEAP @ 1bacd190000HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state000001c771f2ad30 2004 0000  [00]   000001c771f2ad40    20034 - (busy)000001c774a65160 2004 2004  [00]   000001c774a65170    20034 - (busy)000001c774a851a0 2004 2004  [00]   000001c774a851b0    20034 - (busy)000001c774aa51e0 2004 2004  [00]   000001c774aa51f0    20034 - (busy)000001c774ac5220 2004 2004  [00]   000001c774ac5230    20034 - (busy)000001c774ae5260 2004 2004  [00]   000001c774ae5270    20034 - (busy)000001c774b052a0 2004 2004  [00]   000001c774b052b0    20034 - (busy)000001c774b29320 2004 2004  [00]   000001c774b29330    20034 - (busy)000001c774b49360 2004 2004  [00]   000001c774b49370    20034 - (busy)000001c774b693a0 2004 2004  [00]   000001c774b693b0    20034 - (busy)000001c774b893e0 2004 2004  [00]   000001c774b893f0    20034 - (busy)unknown!noop000001c774ba9420 2004 2004  [00]   000001c774ba9430    20034 - (busy)......

block块信息太多,这里我就贴一部分上去,上面列的 HEAP_ENTRY 就是 block 的首地址,然后我通过 dc 一顿找,发现不少下面的输出。


0:000> dc 000001c774a65160 L 50
000001c7`74a65160  dddddddd 00000000 cada2944 0c85cc02  ........D)......
000001c7`74a65170  74a21070 000001c7 74a851b0 000001c7  p..t.....Q.t....
000001c7`74a65180  00000000 00000000 00000000 00000001  ................
000001c7`74a65190  00020000 00000000 0000007a fdfdfdfd  ........z.......
000001c7`74a651a0  00c801aa 55028000 05040355 44b60706  .......UU......D
000001c7`74a651b0  693d6f55 69502c31 32693d6e 7361502c  Uo=i1,Pin=i2,Pas
000001c7`74a651c0  726f7773 33733d64 6f72472c 693d7075  sword=s3,Group=i
000001c7`74a651d0  74532c34 54747261 3d656d69 452c3569  4,StartTime=i5,E
000001c7`74a651e0  6954646e 693d656d 75532c36 41726570  ndTime=i6,SuperA
000001c7`74a651f0  6f687475 657a6972 0a37693d 72657375  uthorize=i7.user
000001c7`74a65200  68747561 7a69726f 2c323d65 3d6e6950  authorize=2,Pin=
000001c7`74a65210  412c3169 6f687475 657a6972 656d6954  i1,AuthorizeTime
000001c7`74a65220  656e6f7a 693d6449 75412c32 726f6874  zoneId=i2,Author
000001c7`74a65230  44657a69 49726f6f 33693d64 6c6f680a  izeDoorId=i3.hol
000001c7`74a65240  79616469 482c333d 64696c6f 693d7961  iday=3,Holiday=i
000001c7`74a65250  6f482c31 6164696c 70795479 32693d65  1,HolidayType=i2
000001c7`74a65260  6f6f4c2c 33693d70 6d69740a 6e6f7a65  ,Loop=i3.timezon
000001c7`74a65270  2c343d65 656d6954 656e6f7a 693d6449  e=4,TimezoneId=i
000001c7`74a65280  75532c31 6d69546e 693d3165 75532c32  1,SunTime1=i2,Su
000001c7`74a65290  6d69546e 693d3265 75532c33 6d69546e  nTime2=i3,SunTim

说实话用dc一个一个找,真的太累,这里我就写一个简单的脚本,把前1w个block都dc出来看看内容咋样?


"use strict";var index = 1;function initializeScript() { return [new host.apiVersionSupport(1, 7)]; }
function log(str) { host.diagnostics.debugLog(str + "\n"); }
function exec(str) { log("\n" + str); return host.namespace.Debugger.Utility.Control.ExecuteCommand(str); }function invokeScript() {show_heap_s();
}function show_heap_s() {//get top 1 var output = exec("!heap -s").Skip(10).First();var h_address = output.split(' ')[0];show_max_blocksize(h_address);
}function show_max_blocksize(address) {var output = exec("!ext.heap -stat -h " + address).Skip(3).First();var block_size = output.trim().split(' ')[0];show_all_blocksize(block_size);
}function show_all_blocksize(blocksize) {var output = exec("!ext.heap -flt s " + blocksize).Take(10000);for (var line of output) {var heap_entry_address = line.trim().split(' ')[0];if (heap_entry_address.indexOf("00") == -1) continue;show_heap_entry(heap_entry_address);}
}function show_heap_entry(heap_entry_address) {var pageIndex = (index++);var path = ".writemem D:\\dumps\\winform-memory-leak\\file\\" + pageIndex + ".txt " + heap_entry_address + " L?0x500";var output = exec(path);log("pageIndex=" + pageIndex);
}

脚本执行后,输出结果如下:

问了下朋友这些字符串大概是干嘛的?为啥非托管中有这么多的string没有得到释放,朋友告诉我这个大概是门禁相关业务,是通过 plc 方式和 C# 进行交互,分析到这里我能提供的信息都已提供了,接下来就要和门禁业务方确认下如何进一步定位和改进了。

三:总结

貌似这是20篇dump案例分享中第一个聊到非托管泄露的问题,曾今我在B站上说只专注于分析.NET托管内存泄漏,看样子很难实现哈,确实 C# 和 lua,C++,COM,内嵌浏览器 的交互造成非托管内存泄漏的例子数不胜数哈 ????????????

END

工作中的你,是否已遇到 ...

1. CPU爆高

2. 内存暴涨

3. 资源泄漏

4. 崩溃死锁

5. 程序呆滞

等紧急事件,全公司都指望着你能解决...  危难时刻才能展现你的技术价值,作为专注于.NET高级调试的技术博主,欢迎微信搜索: 一线码农聊技术,免费协助你分析Dump文件,希望我能将你的踩坑经验分享给更多的人。

记一次 .NET 某智慧水厂API 非托管内存泄漏分析相关推荐

  1. 记一次 .NET 某桌面奇侠游戏 非托管内存泄漏分析

    更多高质量干货:参见我的 GitHub: dotnetfly 一:背景 1. 讲故事 说实话,这篇dump我本来是不准备上一篇文章来解读的,但它有两点深深的感动了我. 无数次的听说用 Unity 可做 ...

  2. 记一次 .NET 某消防物联网 后台服务 内存泄漏分析

    一:背景 1. 讲故事 去年十月份有位朋友从微信找到我,说他的程序内存要炸掉了...截图如下: 时间有点久,图片都被清理了,不过有点讽刺的是,自己的程序本身就是做监控的,结果自己出了问题,太尴尬了

  3. 记一次 .NET 某外贸Web站 内存泄漏分析

    一:背景 1. 讲故事 上周四有位朋友加wx咨询他的程序内存存在一定程度的泄漏,并且无法被GC回收,最终机器内存耗尽,很尴尬. 沟通下来,这位朋友能力还是很不错的,也已经做了初步的dump分析,发现了 ...

  4. 记一次 .NET 某HIS系统后端服务 内存泄漏分析

    一:背景 1. 讲故事 前天那位 his 老哥又来找我了,上次因为CPU爆高的问题我给解决了,看样子对我挺信任的,这次另一个程序又遇到内存泄漏,希望我帮忙诊断下. 其实这位老哥技术还是很不错的,他既然 ...

  5. python 内存泄漏分析工具_记一次调试python内存泄露的问题

    这两天由于公司需要, 自己编写了一个用于接收dicom文件(医学图像文件)的server. 经过各种coding-debuging-coding-debuging之后, 终于上线了, 上线后心里美滋滋 ...

  6. 托管代码C#调用非托管C++ API, 封送嵌套结构体数组

    一.前言: 最近这两天由于项目需要,提供给客户的C++ 动态库需要返回自定义结构体数组,网上也查了很多资料, 推荐一本书, <精通.NET互操作:P/Invoke.C++ Interop和COM ...

  7. 智石案例 | 智慧水厂高精度可视化安全导视应用

    01 项目背景 由深圳水务牵头打造的智慧水厂,利用物联网技术将整个水厂内的"人.货.场.车"连接在一起.在整个水厂建设中,安全生产是政府.企业的重要环节,如何做好安全生产.提升企业 ...

  8. 记一次 .NET医疗布草API程序 内存暴涨分析

    一:背景 1. 讲故事 我在年前写过一篇关于CPU爆高的分析文章 再记一次 应用服务器 CPU 暴高事故分析 ,当时是给同济做项目升级,看过那篇文章的朋友应该知道,最后的结论是运维人员错误的将 IIS ...

  9. 记一次SOFA内存泄漏排查过程

    记一次内存泄漏排查过程 起因 某天中午大家还在安静的午休,睡得正香的时候突然被一阵手机滴-滴滴直响短信惊醒.一看是应用的服务器告警并且对应服务的所有机器都在告警"健康检查失败,自动拉下线&q ...

最新文章

  1. windows优化大师8周年纪念版_《数码宝贝》20周年纪念:当年的八神太一与亚古兽你还记得吗?...
  2. Linux下使用expect实现跳板机自动跳转/免密登录/自动登录(转)
  3. 春天遇见Apache Hadoop
  4. 一个搜索框多个按钮_网站搜索栏设计指南:要不要?怎么设计?
  5. JAVA 临时变量的生命周期_C++临时变量的生命周期?
  6. 逻辑回归分类python实例_Python逻辑回归原理及实际案例应用
  7. php软件开发--yii2框架搭建及gitlib同步线上网站
  8. 中国移民去哪儿 新京报
  9. 火星地形地貌图,摄影:“祝融号”火星车
  10. 平均年薪 15 万,超 6 成本科学历,程序员薪资调查报告大曝光!
  11. 计算与推断思维 十三、预测
  12. vbox虚拟机 做服务器,vbox虚拟机设置服务器(vbox虚拟机做服务器)
  13. 房屋水电煤气省钱秘籍
  14. 玩转OpenCV之一:关于OpenCV学习版本的选择
  15. oracle erp日志,错误,什么地方看日志
  16. rvm安装和安装失败问题解决,使用rvm安装ruby
  17. 【DX-BT24蓝牙模块连接Arduino与手机透传教程】
  18. 记:应聘深信服科技 C++工程师(南京)
  19. CAS-01 关于CAS服务端设置自定义返回参数给CAS-CLIENT
  20. 哪些是当你成为老板才知道的道理?

热门文章

  1. 利用Asp.net中的AJAX制作网页上自动选取开始日期及结束日期的用户自定义控件...
  2. C#打造自己的文件浏览器
  3. PHP 利用Mail_MimeDecode类提取邮件正文
  4. Spring实战Day2
  5. linux如何查看所有的用户(user)、用户组(group)、密码(password/passwd)
  6. 一 流程控制之if判断
  7. 【题解】洛谷P1066 [NOIP2006TG] 2^k进制数(复杂高精+组合推导)
  8. 【BZOJ】1013 球形空间产生器
  9. cannot fetch plan for SQL_ID: 5qgz1p0cut7mx, CHILD_NUMBER: 0
  10. Atitit。 《吠陀》 《梨俱吠陀》overview 经读后感  是印度上古时期一些文献的总称...