记一次 .NET某游戏币自助机后端 内存暴涨分析
一:背景
1. 讲故事
前些天有位朋友找到我,说他们的程序内存会偶发性暴涨,自己分析了下是非托管内存问题,让我帮忙看下怎么回事?哈哈,看到这个dump我还是非常有兴趣的,居然还有这种游戏币自助机类型的程序,下次去大玩家看看他们出币的机器后端是不是C#写的?由于dump是linux上的程序,刚好windbg可以全平台分析,太爽了,直接用windbg开干吧。
二:WinDbg 分析
1. 到底是哪里的泄漏
在 windows 平台上相信有很多朋友都知道用 !address -summary
命令看,但这是专属于windows平台的命令,在分析linux上的dump不好使,参考如下输出:
0:000> !address -summary
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
<unknown> 1685 7ffc`d6725c00 ( 127.988 TB) 100.00% 100.00%
Image 7102 0`0b524400 ( 181.142 MB) 0.00% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
2248 7ffc`02549000 ( 127.984 TB) 100.00%
MEM_PRIVATE 6539 0`df701000 ( 3.491 GB) 0.00% 0.00%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
2248 7ffc`02549000 ( 127.984 TB) 100.00% 100.00%
MEM_COMMIT 6539 0`df701000 ( 3.491 GB) 0.00% 0.00%
--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE 2099 0`dd75e000 ( 3.460 GB) 0.00% 0.00%
PAGE_EXECUTE_WRITECOPY 33 0`00d4c000 ( 13.297 MB) 0.00% 0.00%
PAGE_READONLY 2736 0`00b01000 ( 11.004 MB) 0.00% 0.00%
PAGE_EXECUTE_READ 1671 0`00756000 ( 7.336 MB) 0.00% 0.00%
--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
<unknown> 0`00000000 55cb`2dc3b000 ( 85.794 TB)
Image 7f71`9dbdd000 0`01b16000 ( 27.086 MB)
卦中的内存段分类用处不大,也没有多大的参考价值,那怎么办呢?其实 coreclr 团队也考虑到了这个情况,它提供了一个 maddress 命令来实现跨平台的 !address
,更改后输出如下:
0:000> !sos maddress
Enumerating and tagging the entire address space and caching the result...
Subsequent runs of this command should be faster.
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Memory Kind | StartAddr | EndAddr-1 | Size | Type | State | Protect | Image |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Stack | 7f6e356ec000 | 7f6e35eec000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | |
| Stack | 7f6e35eed000 | 7f6e366ed000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | |
| Stack | 7f6e366ee000 | 7f6e36eee000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | |
| Stack | 7f6e36eef000 | 7f6e376ef000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | |
...
+-------------------------------------------------------------------------+
| Memory Type | Count | Size | Size (bytes) |
+-------------------------------------------------------------------------+
| Stack | 423 | 3.29gb | 3,528,859,648 |
| Image | 7,102 | 181.14mb | 189,940,736 |
| PAGE_READWRITE | 206 | 89.18mb | 93,511,680 |
| GCHeap | 3 | 37.75mb | 39,587,840 |
| HighFrequencyHeap | 395 | 24.66mb | 25,858,048 |
| LowFrequencyHeap | 316 | 22.20mb | 23,277,568 |
| LoaderCodeHeap | 13 | 17.00mb | 17,825,792 |
| ResolveHeap | 2 | 732.00kb | 749,568 |
| HostCodeHeap | 8 | 668.00kb | 684,032 |
| DispatchHeap | 1 | 196.00kb | 200,704 |
| PAGE_EXECUTE_WRITECOPY | 6 | 184.00kb | 188,416 |
| CacheEntryHeap | 3 | 164.00kb | 167,936 |
| IndirectionCellHeap | 3 | 152.00kb | 155,648 |
| LookupHeap | 3 | 144.00kb | 147,456 |
| StubHeap | 2 | 76.00kb | 77,824 |
| PAGE_EXECUTE_READ | 1 | 4.00kb | 4,096 |
+-------------------------------------------------------------------------+
| [TOTAL] | 8,487 | 3.65gb | 3,921,236,992 |
+-------------------------------------------------------------------------+
从卦中可以看到当前程序总计 3.65G
内存占用,基本上都被线程栈给吃掉了,更让人意想不到的是这个线程栈居然占用 8M 的内存空间,这个着实有点大了,而且 linux 不像 windows 有一个 reserved 的概念,这里的 8M 是实实在在的预占,可以观察这 8M 的内存地址即可,都是初始化的 0, 这就说不过去了。
0:000> dp 7f6e356ec000 7f6e35eec000
00007f6e`356ec000 00000000`00000000 00000000`00000000
...
00007f6e`35eebfc0 00000000`00000000 00000000`00000000
00007f6e`35eebfd0 00000000`00000000 00000000`00000000
00007f6e`35eebfe0 00000000`00000000 00000000`00000000
00007f6e`35eebff0 00000000`00000000 00000000`00000000
2. 如何修改栈空间大小
一般来说不同的操作系统发行版有不同的默认栈空间配置,可以先到内存搜一下当前是哪一个发行版,做法就是搜索操作系统名称主要关键字。
0:000> s-a 0 L?0xffffffffffffffff "centos"
...
000055cb`2ecf08c8 63 65 6e 74 6f 73 2e 37-2d 78 36 34 00 00 00 00 centos.7-x64....
...
从卦中可以看到当前操作系统是 centos7-x64
,在 windows 平台上修改栈空间大小可以修改 PE 头,在 linux 上有两种做法。
- 修改 ulimit -s 参数
root@ubuntu:/data# ulimit -s
8192
root@ubuntu:/data# ulimit -s 2048
root@ubuntu:/data# ulimit -s
2048
- 修改 DOTNET_DefaultStackSize 环境变量
DOTNET_DefaultStackSize=180000
更多可以参考文章: https://www.alexander-koepke.de/post/2023-10-18-til-dotnet-stack-size/
上面是解决问题的第一个方向,接下来我们说另一个方向,为什么会产生总计 423 个线程呢?
3. 为什么会有那么多线程
要找到这个答案,需要去看每一个线程此时都在干嘛,这个可以使用 windbg 专属命令。
0:000> ~*e !clrstack
...
OS Thread Id: 0x4e (24)
Child SP IP Call Site
00007F70B20FC4B0 00007f71a4131ad8 [InlinedCallFrame: 00007f70b20fc4b0] /app/Confluent.Kafka.dll!Unknown
00007F70B20FC4B0 00007f7130299970 [InlinedCallFrame: 00007f70b20fc4b0] /app/Confluent.Kafka.dll!Unknown
00007F70B20FC4A0 00007f7130299970 ILStubClass.IL_STUB_PInvoke(IntPtr, IntPtr)
00007F70B20FC530 00007f7130309fab /app/Confluent.Kafka.dll!Unknown
00007F70B20FC880 00007f7131c5a75d /app/Confluent.Kafka.dll!Unknown
00007F70B20FC8A0 00007f7130303ebe /app/DotNetCore.CAP.Kafka.dll!Unknown
00007F70B20FC980 00007f71302f4854 /app/DotNetCore.CAP.dll!Unknown
00007F70B20FCA50 00007f7129b187f4 System.Threading.Tasks.Task.InnerInvoke() [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2387]
00007F70B20FCA70 00007f7129b1d316 System.Threading.Tasks.Task+c.<.cctor>b__272_0(System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2375]
00007F70B20FCA80 00007f7129b03d6b System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs @ 183]
00007F70B20FCAD0 00007f7129b18524 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2333]
00007F70B20FCB50 00007f7129b18418 System.Threading.Tasks.Task.ExecuteEntryUnsafe(System.Threading.Thread) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2271]
00007F70B20FCB70 00007f7129b21a67 System.Threading.Tasks.ThreadPoolTaskScheduler+c.<.cctor>b__10_0(System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs @ 35]
00007F70B20FCB80 00007f7129af88c2 System.Threading.Thread.StartCallback() [/_/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 105]
00007F70B20FCCF0 00007f71a37ab9c7 [DebuggerU2MCatchHandlerFrame: 00007f70b20fccf0]
...
从卦中数据看有很多的 Unknown
,说明dump取得不好,可能不是用正规的 dotnet-dump 或者 procdump,但不管怎么说,还是可以看到大量的和 Kafka 有关的链接库,并且从 InnerInvoke
这个执行 m_action 来看,应该是有大量线程卡在 Kafka 中的某个函数上。
有了这些知识,最后给到朋友的建议如下:
- 修改 DOTNET_DefaultStackSize 参数
可以仿照 windows 上的 .netcore 默认 1.5M 的栈空间设置,因为8M真的太大了,扛不住,也和 Linux 的低内存使用不符。
- 观察 Kafka 的相关逻辑
毕竟有大量线程在 Kafka 的等待上,个人觉得可能是订阅线程太多,或者什么业务执行时间长导致的线程饥饿,尽量把线程压下去。
三:总结
Linux 上的 .NET 调试生态在日渐丰富,这是一件让人很兴奋的事情,最后再给 WinDbg 点个赞,它不仅可以全平台dump分析,还可以实时调试 Linux 进程,现如今的WinDbg真的是神一般的存在。
记一次 .NET某游戏币自助机后端 内存暴涨分析的更多相关文章
- 记一次 .NET 某招聘网后端服务 内存暴涨分析
一:背景 1. 讲故事 前段时间有位朋友wx找到我,说他的程序存在内存阶段性暴涨,寻求如何解决,和朋友沟通下来,他的内存平时大概是5G 左右,在某些时点附近会暴涨到 10G+, 画个图大概就是这样. ...
- 记一次 .NET 某企业OA后端服务 卡死分析
一:背景 1.讲故事 前段时间有位朋友微信找到我,说他生产机器上的 Console 服务看起来像是卡死了,也不生成日志,对方也收不到我的httpclient请求,不知道程序出现什么情况了,特来寻求帮助 ...
- 记一次 .NET医疗布草API程序 内存暴涨分析
一:背景 1. 讲故事 我在年前写过一篇关于CPU爆高的分析文章 再记一次 应用服务器 CPU 暴高事故分析 ,当时是给同济做项目升级,看过那篇文章的朋友应该知道,最后的结论是运维人员错误的将 IIS ...
- 记一次 .NET 某外贸Web站 内存泄漏分析
一:背景 1. 讲故事 上周四有位朋友加wx咨询他的程序内存存在一定程度的泄漏,并且无法被GC回收,最终机器内存耗尽,很尴尬. 沟通下来,这位朋友能力还是很不错的,也已经做了初步的dump分析,发现了 ...
- 记一次 .NET 某三甲医院HIS系统 内存暴涨分析
一:背景 1. 讲故事 前几天有位朋友加wx说他的程序遭遇了内存暴涨,求助如何分析? 和这位朋友聊下来,这个dump也是取自一个HIS系统,如朋友所说我这真的是和医院杠上了,这样也好,给自己攒点资源, ...
- 记一次 .NET 某WMS仓储打单系统 内存暴涨分析
一:背景 1. 讲故事 七月中旬有一位朋友加wx求助,他的程序在生产上跑着跑着内存就飙起来了,貌似没有回头的趋势,询问如何解决,截图如下: 和这位朋友聊下来,感觉像是自己在小县城当了个小老板,规律的生 ...
- 记一次 .NET 某电厂Web系统 内存泄漏分析
一:背景 1. 讲故事 前段时间有位朋友找到我,说他的程序内存占用比较大,寻求如何解决,截图就不发了,分析下来我感觉除了程序本身的问题之外,.NET5 在内存管理方面做的也不够好,所以有必要给大家分享 ...
- 记一次 .NET 某RFID标签管理系统 CPU 暴涨分析
一:背景 1. 讲故事 前段时间有位朋友说他的程序 CPU 出现了暴涨现象,由于程序是买来的,所以问题就比较棘手了,那既然找到我,就想办法帮朋友找出来吧,分析下来,问题比较经典,有必要和大家做一下分享 ...
- 记一次 .NET 某工控软件 内存泄露分析
一:背景 1.讲故事 上个月 .NET调试训练营 里的一位老朋友给我发了一个 8G 的dump文件,说他的程序内存泄露了,一时也没找出来是哪里的问题,让我帮忙看下到底是怎么回事,毕竟有了一些调试功底也 ...
- 记一次 .NET某家装ERP系统 内存暴涨分析
一:背景 1. 讲故事 前段时间微信上有一位老朋友找到我,说他的程序跑着跑着内存会突然爆高,有时候会下去,有什么会下不去,怀疑是不是某些情况下存在内存泄露,让我帮忙分析一下,其实内存泄露方面的问题还是 ...
随机推荐
- Apsara Stack 技术百科 | 浅谈阿里云混合云新一代运维平台演进与实践
简介:随着企业业务规模扩大和复杂化及云计算.大数据等技术的不断发展,大量传统企业希望用上云来加速其数字化转型,以获得虚拟化.软件化.服务化.平台化的红利.在这个过程中,因为软件资产规模持续增大而导致 ...
- 技术解析:一文看懂 Anolis OS 国密生态 | 龙蜥专场
简介: Anolis OS国密是社区在Anolis OS上做的国密技术解决方案. 编者注:本文系两位演讲者整理,他们在2021年阿里云开发者大会的「开源操作系统社区和生态分论坛」上带了分享,演讲主 ...
- B/S 结构系统的 缓存机制(Cookie) 以及基于 cookie 机制实现 oa 十天免登录的功能
B/S 结构系统的 缓存机制(Cookie) 以及基于 cookie 机制实现 oa 十天免登录的功能 @ 目录 B/S 结构系统的 缓存机制(Cookie) 以及基于 cookie 机制实现 oa ...
- 七、Doris Colocation Join
Colocation Join 是在 Doris 0.9 版本中引入的新功能.旨在为某些 Join 查询提供本地性优化,来减少数据在节点间的传输耗时,加速查询. 1.基本理论 Join 的常见连接类型 ...
- HZ2023 远足游记
你说得对,但是我放假之前写的 P4689 代码没了 所以来摆 4.6(远足) 上午 走路,刚开始感觉没啥 走到园博园发现没预料中那么顺利 但是还感觉没啥 因为也没预料到 \(N·m\) 学校会让我们原 ...
- virutalenvwrapper安装和使用
目录 virutalenvwrapper安装和使用 目的: 1.安装pip 2.安装virutalenv和virutalenvwrapper 3.配置环境变量 4. 创建虚拟环境 5.列出全部的虚拟环 ...
- Golang从入门到微服务
学习视频: https://www.bilibili.com/video/BV1Sg411T7TV?p=69 学习资料下载: 链接: https://pan.baidu.com/s/1Yk4GemFR ...
- TCP/IP协议栈及网络基础
TCP/IP协议栈及网络基础 目录 TCP/IP协议栈及网络基础 1. TCP/IP协议栈及网络基础 1.1 OSI网络模型 1.2 TCP/IP网络模型 1.2.1 物理层 1.2.2 数据链路层 ...
- fastposter v2.8.4 发布 电商海报生成器
fastposter v2.8.4 发布 电商海报生成器 fastposter海报生成器,电商海报编辑器,电商海报设计器,fast快速生成海报 海报制作 海报开发.贰维海报,图片海报,分享海报贰维码推 ...
- 《最新出炉》系列入门篇-Python+Playwright自动化测试-46-鼠标滚轮操作
1.简介 有些网站为了节省流量和资源,提高加载效率,采用的是动态加载(懒加载)的,也就是当拖动页面右侧滚动条后会自动加载网页下面的内容,不拖动就不会加载的或者通过鼠标滚轮操作. 2.wheel模拟鼠标 ...