记一次 .NET某道闸收费系统 内存溢出分析

一:背景

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: nonesegment     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 0x04381000segment     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 误用就对程序造成了毁灭性的打击,这也警示大家在用某个属性某个方法前,一定要先搞清楚它到底是怎么玩的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/631225.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Openwrt 下动态路由协议(quagga-OSPF)配置与验证

文章目录 前言网络拓扑静态路由方式动态路由方式Openwrt下 Quagga 安装Quagga 配置R1路由器zebra配置R1路由器ospf配置R2路由器zebra配置R2路由器ospf配置OSPF协议分析REF本文将在两台openwrt系统上安装配置quagga, 搭建一套完整环境,来验证OSPF动态路由的基本功能和实际效果,…

面经-redis缓存

什么是Redis Redis(Remote Dictionary Server)键只能为字符串&#xff0c;值&#xff1a;字符串、列表、集合、散列表、有序集合。Redis 用来做分布式锁。支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。 Redis为什么这么快 完全基于内存&#xff0c;数据结构简单…

深度学习记录--正则化(regularization)

什么是正则化&#xff1f; 正则化(regularization)是一种实用的减少方差(variance)的方法&#xff0c;也即避免过度拟合 几种正则化的方法 L2正则化 又被称为权重衰减(weight dacay) 在成本函数中加上正则项&#xff1a; 其中 由于在w的更新过程中会递减&#xff0c;即权…

2024年继续看好英伟达的两个理由

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 2023年是英伟达业务爆发式增长的一年 2023年可以说是英伟达成立近30年以来最好的一年。由于大语言模型带动的训练和推理算力需求的增加&#xff0c;导致市场对英伟达AI芯片(H100、A100等)和HGX平台的采购需求也出现了飙升…

鸿蒙Harmony-页面路由(router)详解

慢慢理解世界&#xff0c;慢慢更新自己&#xff0c;希望你的每一个昨天&#xff0c;今天&#xff0c;和明天都会很快乐&#xff0c;你知道的&#xff0c;先好起来的从来都不是生活&#xff0c;而是你自己 目录 一&#xff0c;定义 二&#xff0c;页面跳转 2.1使用router.pushU…

【Java】HttpServlet类中前后端交互三种方式(query string、form表单、JSON字符串)

在前后端的交互中&#xff0c;前端通过以下三种方式来与后端进行交互&#x1f31f; ✅query string ✅form表单 ✅JSON字符串 下面我们将书写这三种方式的后端代码并进行讲解 1、Query String QueryString即在url中写入键值对&#xff0c;一般用doGet方法进行交互 代码如下 …

2018年认证杯SPSSPRO杯数学建模D题(第一阶段)投篮的最佳出手点全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 对于投篮最佳出手点的探究 D题 投篮的最佳出手点 原题再现&#xff1a; 影响投篮命中率的因素不仅仅有出手角度、球感、出手速度&#xff0c;还有出手点的选择。规范的投篮动作包含两膝微屈、重心落在两脚掌上、下肢蹬地发力、身体随之向前上…

H3C校园网双出口配置

H3C校园网双出口配置 &#x1f3c6;荣誉认证&#xff1a;51CTO博客专家博主、TOP红人、明日之星&#xff1b;阿里云开发者社区专家博主、技术博主、星级博主。 &#x1f4bb;微信公众号&#xff1a;微笑的段嘉许 &#x1f4cc;本文由微笑的段嘉许原创&#xff01; &#x1f389…

服务器推送数据你还在用 WebSocket么?

当涉及到推送数据时,人们首先会想到 WebSocket。 的确,WebSocket 允许双向通信,可以自然地用于服务器到浏览器的消息推送。 然而,如果只需要单向的消息推送,HTTP 通过服务器发送的事件也有这种功能。 WebSocket 的通信过程如下: 首先,通过 HTTP 切换协议。服务器返回 101 状…

驾驭数字孪生:智慧水利的未来之路

一、数字孪生技术的原理与实践 随着科技的不断进步&#xff0c;数字孪生技术作为一项创新的技术应用&#xff0c;正在逐渐改变我们的生活和工作方式。特别是在工业领域&#xff0c;数字孪生技术被视为实现智能制造、提升生产效率和产品质量的重要手段。本章节将深入探讨数字孪…

k8s的对外服务--ingress

service作用体现在两个方面 1、集群内部 不断跟踪pod的变化&#xff0c;更新endpoint中的pod对象&#xff0c;基于pod的IP地址不断变化的一种服务发现机制 2、集群外部 类似负载均衡器&#xff0c;把流量ip端口&#xff0c;不涉及转发url&#xff08;http&#xff0c;https&a…

npm依赖库备份

常用命令 设置默认使用本地缓存安装Nodejs时会自动安装npm&#xff0c;但是局路径是C:\Users\Caffrey\AppData\Roaming\npm默认的缓存路径是C:\Users\Caffrey\AppData\Roaming\npm-cache&#xff1b;查看npm的prefix和cache路径配置信息设置路径 设置默认使用本地缓存 npm con…

本周五上海见 第二届证券基金行业先进计算技术大会暨2024低时延技术创新实践论坛(上海站)即将召开

低时延技术是证券基金期货领域业务系统的核心技术&#xff0c;是打造极速交易系统领先优势的关键&#xff0c;也是证券基金行业关注的前沿技术热点。 1月19日下午&#xff0c;第二届证券基金行业先进计算技术大会暨2024低时延技术创新实践论坛&#xff08;上海站&#xff09;即…

springmvc上传与下载

文件上传 结构图 导入依赖 <dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId…

MySQL之视图索引

学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课表&#xff1a;SC (Sno, Cno, Score)…

VSCODE使用CMAKE显示命令无法找到

背景&#xff1a;使用了code server&#xff0c;安装CMAKE和CMAKE TOOLS&#xff0c;但是通过ctrlshiftp打开命令面板&#xff0c;运行随便一个cmake指令&#xff0c;都出现了指令无法找到。具体为“命令"CMake: 配置"导致错误 (command ‘cmake.configure’ not fou…

GPT APP的开发步骤

开发一个GPT&#xff08;Generative Pre-trained Transformer&#xff09; Store&#xff08;存储&#xff09;涉及到使用预训练的语言模型&#xff08;例如GPT-3&#xff09;来生成和管理内容。以下是一般的步骤&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&…

探索设计模式的魅力:抽象工厂模式的艺术

抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;用于在不指定具体类的情况下创建一系列相关或相互依赖的对象。它提供了一个接口&#xff0c;用于创建一系列“家族”或相关依赖对象&#xff0c;而无需指定它们的具体类。 主要参…

[足式机器人]Part2 Dr. CAN学习笔记- Kalman Filter卡尔曼滤波器Ch05-1+2

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Kalman Filter卡尔曼滤波器 Ch05-12 1. Recursive Algirithm 递归算法2. Data Fusion 数据融合Covarince Matrix协方差矩阵State Space状态空间方程 Observation观测器 1. Recursive Algirithm…

docker环境下mongo副本集的部署及异常修复

最近更换了办公地点。部署在本地docker环境里的mongo数据库不能使用了。原因是本地的ip地址变更。以前的mongo副本集的配置需要更新。处理完后&#xff0c;索性重新记录一下mongo副本集在docker中的部署流程。 mongo的事务及副本集 我们先了解一下什么是事务&#xff0c;事务…