经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
记一次 .NET某游戏币自助机后端 内存暴涨分析
来源:cnblogs  作者:一线码农  时间:2024/6/12 16:20:26  对本文有异议

一:背景

1. 讲故事

前些天有位朋友找到我,说他们的程序内存会偶发性暴涨,自己分析了下是非托管内存问题,让我帮忙看下怎么回事?哈哈,看到这个dump我还是非常有兴趣的,居然还有这种游戏币自助机类型的程序,下次去大玩家看看他们出币的机器后端是不是C#写的?由于dump是linux上的程序,刚好windbg可以全平台分析,太爽了,直接用windbg开干吧。

二:WinDbg 分析

1. 到底是哪里的泄漏

在 windows 平台上相信有很多朋友都知道用 !address -summary 命令看,但这是专属于windows平台的命令,在分析linux上的dump不好使,参考如下输出:

  1. 0:000> !address -summary
  2. --- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
  3. <unknown> 1685 7ffc`d6725c00 ( 127.988 TB) 100.00% 100.00%
  4. Image 7102 0`0b524400 ( 181.142 MB) 0.00% 0.00%
  5. --- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
  6. 2248 7ffc`02549000 ( 127.984 TB) 100.00%
  7. MEM_PRIVATE 6539 0`df701000 ( 3.491 GB) 0.00% 0.00%
  8. --- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
  9. 2248 7ffc`02549000 ( 127.984 TB) 100.00% 100.00%
  10. MEM_COMMIT 6539 0`df701000 ( 3.491 GB) 0.00% 0.00%
  11. --- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
  12. PAGE_READWRITE 2099 0`dd75e000 ( 3.460 GB) 0.00% 0.00%
  13. PAGE_EXECUTE_WRITECOPY 33 0`00d4c000 ( 13.297 MB) 0.00% 0.00%
  14. PAGE_READONLY 2736 0`00b01000 ( 11.004 MB) 0.00% 0.00%
  15. PAGE_EXECUTE_READ 1671 0`00756000 ( 7.336 MB) 0.00% 0.00%
  16. --- Largest Region by Usage ----------- Base Address -------- Region Size ----------
  17. <unknown> 0`00000000 55cb`2dc3b000 ( 85.794 TB)
  18. Image 7f71`9dbdd000 0`01b16000 ( 27.086 MB)

卦中的内存段分类用处不大,也没有多大的参考价值,那怎么办呢?其实 coreclr 团队也考虑到了这个情况,它提供了一个 maddress 命令来实现跨平台的 !address,更改后输出如下:

  1. 0:000> !sos maddress
  2. Enumerating and tagging the entire address space and caching the result...
  3. Subsequent runs of this command should be faster.
  4. +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  5. | Memory Kind | StartAddr | EndAddr-1 | Size | Type | State | Protect | Image |
  6. +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  7. | Stack | 7f6e356ec000 | 7f6e35eec000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | |
  8. | Stack | 7f6e35eed000 | 7f6e366ed000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | |
  9. | Stack | 7f6e366ee000 | 7f6e36eee000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | |
  10. | Stack | 7f6e36eef000 | 7f6e376ef000 | 8.00mb | MEM_PRIVATE | MEM_COMMIT | PAGE_READWRITE | |
  11. ...
  12. +-------------------------------------------------------------------------+
  13. | Memory Type | Count | Size | Size (bytes) |
  14. +-------------------------------------------------------------------------+
  15. | Stack | 423 | 3.29gb | 3,528,859,648 |
  16. | Image | 7,102 | 181.14mb | 189,940,736 |
  17. | PAGE_READWRITE | 206 | 89.18mb | 93,511,680 |
  18. | GCHeap | 3 | 37.75mb | 39,587,840 |
  19. | HighFrequencyHeap | 395 | 24.66mb | 25,858,048 |
  20. | LowFrequencyHeap | 316 | 22.20mb | 23,277,568 |
  21. | LoaderCodeHeap | 13 | 17.00mb | 17,825,792 |
  22. | ResolveHeap | 2 | 732.00kb | 749,568 |
  23. | HostCodeHeap | 8 | 668.00kb | 684,032 |
  24. | DispatchHeap | 1 | 196.00kb | 200,704 |
  25. | PAGE_EXECUTE_WRITECOPY | 6 | 184.00kb | 188,416 |
  26. | CacheEntryHeap | 3 | 164.00kb | 167,936 |
  27. | IndirectionCellHeap | 3 | 152.00kb | 155,648 |
  28. | LookupHeap | 3 | 144.00kb | 147,456 |
  29. | StubHeap | 2 | 76.00kb | 77,824 |
  30. | PAGE_EXECUTE_READ | 1 | 4.00kb | 4,096 |
  31. +-------------------------------------------------------------------------+
  32. | [TOTAL] | 8,487 | 3.65gb | 3,921,236,992 |
  33. +-------------------------------------------------------------------------+

从卦中可以看到当前程序总计 3.65G 内存占用,基本上都被线程栈给吃掉了,更让人意想不到的是这个线程栈居然占用 8M 的内存空间,这个着实有点大了,而且 linux 不像 windows 有一个 reserved 的概念,这里的 8M 是实实在在的预占,可以观察这 8M 的内存地址即可,都是初始化的 0, 这就说不过去了。

  1. 0:000> dp 7f6e356ec000 7f6e35eec000
  2. 00007f6e`356ec000 00000000`00000000 00000000`00000000
  3. ...
  4. 00007f6e`35eebfc0 00000000`00000000 00000000`00000000
  5. 00007f6e`35eebfd0 00000000`00000000 00000000`00000000
  6. 00007f6e`35eebfe0 00000000`00000000 00000000`00000000
  7. 00007f6e`35eebff0 00000000`00000000 00000000`00000000

2. 如何修改栈空间大小

一般来说不同的操作系统发行版有不同的默认栈空间配置,可以先到内存搜一下当前是哪一个发行版,做法就是搜索操作系统名称主要关键字。

  1. 0:000> s-a 0 L?0xffffffffffffffff "centos"
  2. ...
  3. 000055cb`2ecf08c8 63 65 6e 74 6f 73 2e 37-2d 78 36 34 00 00 00 00 centos.7-x64....
  4. ...

从卦中可以看到当前操作系统是 centos7-x64,在 windows 平台上修改栈空间大小可以修改 PE 头,在 linux 上有两种做法。

  • 修改 ulimit -s 参数
  1. root@ubuntu:/data# ulimit -s
  2. 8192
  3. root@ubuntu:/data# ulimit -s 2048
  4. root@ubuntu:/data# ulimit -s
  5. 2048
  • 修改 DOTNET_DefaultStackSize 环境变量
  1. DOTNET_DefaultStackSize=180000

更多可以参考文章: https://www.alexander-koepke.de/post/2023-10-18-til-dotnet-stack-size/

上面是解决问题的第一个方向,接下来我们说另一个方向,为什么会产生总计 423 个线程呢?

3. 为什么会有那么多线程

要找到这个答案,需要去看每一个线程此时都在干嘛,这个可以使用 windbg 专属命令。

  1. 0:000> ~*e !clrstack
  2. ...
  3. OS Thread Id: 0x4e (24)
  4. Child SP IP Call Site
  5. 00007F70B20FC4B0 00007f71a4131ad8 [InlinedCallFrame: 00007f70b20fc4b0] /app/Confluent.Kafka.dll!Unknown
  6. 00007F70B20FC4B0 00007f7130299970 [InlinedCallFrame: 00007f70b20fc4b0] /app/Confluent.Kafka.dll!Unknown
  7. 00007F70B20FC4A0 00007f7130299970 ILStubClass.IL_STUB_PInvoke(IntPtr, IntPtr)
  8. 00007F70B20FC530 00007f7130309fab /app/Confluent.Kafka.dll!Unknown
  9. 00007F70B20FC880 00007f7131c5a75d /app/Confluent.Kafka.dll!Unknown
  10. 00007F70B20FC8A0 00007f7130303ebe /app/DotNetCore.CAP.Kafka.dll!Unknown
  11. 00007F70B20FC980 00007f71302f4854 /app/DotNetCore.CAP.dll!Unknown
  12. 00007F70B20FCA50 00007f7129b187f4 System.Threading.Tasks.Task.InnerInvoke() [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2387]
  13. 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]
  14. 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]
  15. 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]
  16. 00007F70B20FCB50 00007f7129b18418 System.Threading.Tasks.Task.ExecuteEntryUnsafe(System.Threading.Thread) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2271]
  17. 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]
  18. 00007F70B20FCB80 00007f7129af88c2 System.Threading.Thread.StartCallback() [/_/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 105]
  19. 00007F70B20FCCF0 00007f71a37ab9c7 [DebuggerU2MCatchHandlerFrame: 00007f70b20fccf0]
  20. ...

从卦中数据看有很多的 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真的是神一般的存在。
图片名称

原文链接:https://www.cnblogs.com/huangxincheng/p/18243233

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号