一:背景

1. 讲故事

前些天有位朋友找到我,说他的程序几天内存就要爆一次,不知道咋回事,找不出原因,让我帮忙看一下,这种问题分析dump是最简单粗暴了,拿到dump后接下来就是一顿分析。

二:WinDbg 分析

1. 程序为什么会暴

程序既然会爆,可能是虚拟地址受限,也可能是系统内存不足,可以用 !address -summary 观察下。


0:037> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown> 866 53577000 ( 1.302 GB) 69.38% 65.11%
Image 2244 16ee2000 ( 366.883 MB) 19.09% 17.91%
Heap 222 8adc000 ( 138.859 MB) 7.23% 6.78%
Free 460 7e14000 ( 126.078 MB) 6.16%
Stack 255 5150000 ( 81.312 MB) 4.23% 3.97%
TEB 85 db000 ( 876.000 kB) 0.04% 0.04%
Other 20 79000 ( 484.000 kB) 0.02% 0.02%
PEB 1 3000 ( 12.000 kB) 0.00% 0.00%
...
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT 2900 64906000 ( 1.571 GB) 83.72% 78.57%
MEM_RESERVE 793 138d6000 ( 312.836 MB) 16.28% 15.28%
MEM_FREE 460 7e14000 ( 126.078 MB) 6.16%
...

从卦中可以明显的看出,这又是一例经典的32bit程序受到了2G的内存限制,按往期经验来说解决办法比较简单,改成大地址或者x64即可。

哈哈,既然要分享这篇,自然就不是这么简单的事情,这需要我们排查这个溢出是不是程序的bug导致的,如果是那还得继续找原因。

2. 是程序bug导致的吗

要想搞清楚这个问题,需要去分析各处的内存占用,比如托管堆,可以用 !eeheap -gc 观察。


0:037> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x49fd10a8
generation 1 starts at 0x49fd1000
generation 2 starts at 0x03381000
ephemeral segment allocation context: none
segment begin allocated size
03380000 03381000 0437ff88 0xffef88(16773000)
23e60000 23e61000 24e5ff88 0xffef88(16773000)
0b510000 0b511000 0c50ff88 0xffef88(16773000)
...
7be20000 7be21000 7cbbdb60 0xd9cb60(14273376)
49fd0000 49fd1000 4afcfe08 0xffee08(16772616)
Large object heap starts at 0x04381000
segment begin allocated size
04380000 04381000 04a67b50 0x6e6b50(7236432)
Total Size: Size: 0x39738ad4 (963873492) bytes.
------------------------------
GC Heap Size: Size: 0x39738ad4 (963873492) bytes.

从卦中可以看到,托管堆占用963M,并且产生了很多的16M的segment,这就表明当前的托管堆吃掉了内存,接下来的问题是为什么托管堆吃了那么多的内存呢?那就只能用 !dumpheap -stat 去观察下托管堆的对象布局咯。


0:037> !dumpheap -stat
Statistics:
MT Count TotalSize Class Name
...
717c8b4c 264594 11642136 System.Threading.ExecutionContext
717cd044 265930 13034088 System.Collections.Hashtable+bucket[]
717ccff4 265854 13824408 System.Collections.Hashtable
71761c34 268005 17152320 System.Threading.OverlappedData
70d73c10 264469 26446900 System.Net.Sockets.OverlappedAsyncResult
717cdd04 280225 293649193 System.Byte[]
013a9f98 269886 540566904 Free
Total 3880354 objects

从卦中可以看到当前托管堆有 26.8w 的 OverlappedData 对象,这是一个非常明显的异常信号,熟悉这块的朋友应该知道,这个东西常常和异步打交道,也就表示当前程序可能有高达 26.8w 的异步请求可能没有得到响应,要想找到这个答案,就需要对 OverlappedData 进行穿刺。

3. OverlappedData 穿刺检查

OverlappedData 穿刺的目的就是要活检内部的 AsyncCallback 回调函数,看看到底是良性还是恶性的,相关命令如下:


0:037> !dumpheap -stat
...
34f38ac4 71761c34 64
34f39088 71761c34 64
...
0:037> !mdt 34f39088
34f39088 (System.Threading.OverlappedData)
m_asyncResult:33e8aafc (System.Net.Sockets.OverlappedAsyncResult)
m_iocb:03c077a0 (System.Threading.IOCompletionCallback)
...
m_nativeOverlapped:(System.Threading.NativeOverlapped) VALTYPE (MT=7176dfe0, ADDR=34f390b0)
0:037> !mdt 33e8aafc
33e8aafc (System.Net.Sockets.OverlappedAsyncResult)
m_AsyncObject:03c71d44 (System.Net.Sockets.Socket)
m_AsyncState:33e8aaec (xxx)
m_AsyncCallback:03e8f214 (System.AsyncCallback)
...
0:037> !mdt 03e8f214
03e8f214 (System.AsyncCallback)
_target:03c065a8 (xxx)
_methodPtr:19432480 (System.IntPtr)
0:037> u 19432480
19432480 e933932102 jmp 1b64b7b8
19432485 5f pop edi
...
0:037> !ip2md 1b64b7b8
MethodDesc: 131605ac
Method Name: xxxDevices.ReceiveCallback(System.IAsyncResult)

卦中的信息量还是蛮大的,可以看到这是一个和 Socket 相关的异步函数,并且也成功找到了 xxxDevices.ReceiveCallback 回调函数,接下来就是检查下这个方法附近的业务逻辑,由于代码会涉及到一些隐私,我就多模糊一点,请见谅,截图如下:

仔细阅读这段代码,他是想用异步的方式一次次的用byte[1024]去丈量一段可能的大数据,直到这个 Stream 不能再读了,所以用了 if (stream.CanRead) 判断。

对 Socket 编程比较熟悉的朋友相信很快就能发现问题,判断 Stream 中的数据是否读完应该用 DataAvailable 属性,而不是 CanRead,比如下面这段正确的代码:

最后再贴VS中对 CanReadDataAvailable 属性的解释。


//
// Summary:
// Gets a value that indicates whether the System.Net.Sockets.NetworkStream supports
// reading.
//
// Returns:
// true if data can be read from the stream; otherwise, false. The default value
// is true.
public override bool CanRead { get; } //
// Summary:
// Gets a value that indicates whether data is available on the System.Net.Sockets.NetworkStream
// to be read.
//
// Returns:
// true if data is available on the stream to be read; otherwise, false.
//
public virtual bool DataAvailable { get; }

三:总结

这个事故非常有意思,一个简简单单的 CanRead 误用就对程序造成了毁灭性的打击,这也警示大家在用某个属性某个方法前,一定要先搞清楚它到底是怎么玩的。

记一次 .NET某道闸收费系统 内存溢出分析的更多相关文章

  1. 记一次 .NET 医院CIS系统 内存溢出分析

    一:背景 1. 讲故事 前几天有位朋友加wx求助说他的程序最近总是出现内存溢出,很崩溃,如下图: 和这位朋友聊下来,发现他也是搞医疗的,哈哈,.NET 在医疗方面还是很有市场的,不过对于内存方面出的问 ...

  2. 记一次 .NET 某电厂Web系统 内存泄漏分析

    一:背景 1. 讲故事 前段时间有位朋友找到我,说他的程序内存占用比较大,寻求如何解决,截图就不发了,分析下来我感觉除了程序本身的问题之外,.NET5 在内存管理方面做的也不够好,所以有必要给大家分享 ...

  3. eclipse memory analyzer对系统内存溢出堆文件解析0(转)

    前言 在平时工作过程中,有时会遇到OutOfMemoryError,我们知道遇到Error一般表明程序存在着严重问题,可能是灾难性的.所以找出是什么原因造成OutOfMemoryError非常重要.现 ...

  4. eclipse memory analyzer对系统内存溢出堆文件解析(转)

    本文转之:https://blog.csdn.net/rachel_luo/article/details/8992461 前言 性能分析工具之-- Eclipse Memory Analyzer t ...

  5. 记一次 .NET 某妇产医院 WPF内存溢出分析

    一:背景 1. 讲故事 上个月有位朋友通过博客园的短消息找到我,说他的程序存在内存溢出情况,寻求如何解决. 要解决还得通过 windbg 分析啦. 二:Windbg 分析 1. 为什么会内存溢出 大家 ...

  6. 记一次 .NET 某医院HIS系统 CPU爆高分析

    一:背景 1. 讲故事 前几天有位朋友加 wx 抱怨他的程序在高峰期总是莫名其妙的cpu爆高,求助如何分析? 和这位朋友沟通下来,据说这问题困扰了他们几年,还请了微软的工程师过来解决,无疾而终,应该还 ...

  7. 记一次 .NET 某三甲医院HIS系统 内存暴涨分析

    一:背景 1. 讲故事 前几天有位朋友加wx说他的程序遭遇了内存暴涨,求助如何分析? 和这位朋友聊下来,这个dump也是取自一个HIS系统,如朋友所说我这真的是和医院杠上了,这样也好,给自己攒点资源, ...

  8. 记一次 WinDbg 分析 .NET 某工厂MES系统 内存泄漏分析

    一:背景 1. 讲故事 上个月有位朋友加微信求助,说他的程序跑着跑着就内存爆掉了,寻求如何解决,截图如下: 从聊天内容看,这位朋友压力还是蛮大的,话说这貌似是我分析的第三个 MES 系统了,看样子 . ...

  9. 记一次 .NET 某WMS仓储打单系统 内存暴涨分析

    一:背景 1. 讲故事 七月中旬有一位朋友加wx求助,他的程序在生产上跑着跑着内存就飙起来了,貌似没有回头的趋势,询问如何解决,截图如下: 和这位朋友聊下来,感觉像是自己在小县城当了个小老板,规律的生 ...

  10. 记一次 .NET 某智能服装智造系统 内存泄漏分析

    一:背景 1. 讲故事 上个月有位朋友找到我,说他的程序出现了内存泄漏,不知道如何进一步分析,截图如下: 朋友这段话已经说的非常言简意赅了,那就上 windbg 说话吧. 二:Windbg 分析 1. ...

随机推荐

  1. 七天.NET 8操作SQLite入门到实战 - 第六天后端班级管理相关接口完善和Swagger自定义配置

    前言 在上一章节我们在后端框架中引入 SQLite-net ORM 并封装常用方法(SQLiteHelper),今天我们的任务是设计好班级管理相关的表.完善后端班级管理相关接口并对Swagger自定义 ...

  2. ElasticSearch索引生命周期策略配置(ES TTL)

    背景 有些索引数据作为临时数据存放,一段时间后我们希望索引可以自动过期删除,就是常说的TTL(Time To Live)机制 ElasticSearch索引数量过多会占用很多主分片和副本分片,最终导致 ...

  3. VBA常用的函数

    space(8)加空格 vbcrlf换行 trim()去掉两侧空格 lrim()去掉左侧空格 rtrim()去掉右侧空格 left()取字符的左侧 right()取字符串的右侧开始 mid()取字符串 ...

  4. 微调baichuan2-7b遇到的显存坑

    问题描述: 微调baichuan2-7b模型,验证一轮后继续训练第一个iteration显存大幅增加 项目链接: https://github.com/wp931120/baichuan_sft_lo ...

  5. bash shell笔记整理——which和whereis命令

    which和whereis命令作用 which:显示给定命令所在路径 whereis:相比which更完善,显示命令路径.man文件路径(如果有).源代码路径 which语法 which [optio ...

  6. 如何实现CesiumJS的视效升级?

    CesiumJS作为一款强大的地理可视化引擎,为我们提供了丰富的地球数据可视化和交互展示的能力.然而,随着用户需求的不断增加和技术的不断进步,如何进一步提升CesiumJS的视觉效果成为了一个重要的问 ...

  7. NC65单据模板公式使用

    单据模板公式使用 (一) 公式使用场景 用户使用产品时,往往对单据上的字段取值有各种不同的需求.为此单据模板提供 了模板公式功能,可以让实施顾问或者用户通过配置各种公式,并且不用修改代码,从 而满足用 ...

  8. HP LoadRunner 11.00安装+破解+汉化

    里面包含多个破解码,最高支持6.5w个并发: https://blog.csdn.net/xianjie0318/article/details/78625980

  9. 如何用python脚本制作生成CANdbc

    最近在工作中,有同事拿了一个excel的dbc表格,在用官方的dbc工具一个一个创建信号,大概看了一下共累计20多个节点,300多个信号,居然在手动处理,顿感无语.. 于是在网络上搜相关的dbc 通过 ...

  10. 关于腾讯地图geolocation.getLocation 经常定位失败,定位时间过长的解决方法

    今天遇到个项目,腾讯地图定位出现问题,导致地图无法呈现出最近的目标 这是正常的效果,之前一直出现贵州等地点的信息,查看控制台的网络后,发现腾讯的定位失败,要么就是定位时间过长,要20S左右,但是换ED ...