如果能帮助我理解内存转储的大小,我将不胜感激。背景:我有一个net 7控制台应用程序(主应用程序)在容器中运行,它与另一个net 7控制台应用程序(sidecar)一起托管在Kubernetes中。在测试场景下,主应用程序的内存是~570MB
,我得到了一个完整的内存转储(使用procdump for Linux或dotnet dump).基于目标环境,我需要减少容器的内存,并开始分析内存中的内容,以了解是什么消耗了这么多。
所有GC堆的合并大小:94 MB模块大小:69,3 MB本机堆大小:51.8 MB嵌入式资源(例如位图):0线程数:49(总数),20(活动)
我的粗略计算是:GCHeap(94)+ 2* Modules(69,3)+ Native Heap(51,8)+1 MB * Threads(49)= 333,4MB
(我将Modules的大小加倍,作为jitted assembly的近似值)
-->我预期的内存使用量和实际消耗的内存之间的差异是~ 238MB (42%)
我错过了什么?有人能告诉我任何文档/工具/等,以了解意外的内存是从哪里来的?
我试着在dotMemory和dotnet dump中分析转储,但卡住了,因为我发现的数字看起来不错,但总内存消耗太高。
编辑(1):来自dotMemory Heap Fragmentation的屏幕截图:
x1c 0d1x的数据
2条答案
按热度按时间yiytaume1#
一些内存可能被托管堆保留,但由于堆碎片而未使用。值“GCHeap 94MB”包括完整堆大小还是仅包括分配对象的大小?请在dotMemory中打开此转储并附上
Inspections | Heap fragmentation
部分的屏幕截图?zwghvu4y2#
我试着回答我自己的问题,并列出我的学习:
(1)核心转储还包含dotnet使用的本机库,这些库可以分配自己的内存页面(自己的堆)。对于我的应用程序,加载了以下库:
| 名称|大小[KiB]|
| --|--|
| /usr/lib/x86_64-linux-gnu/libicudata.so.67.1| 27748 |
| /usr/lib/x86_64-linux-gnu/libicui18n.so.67.1| 3092 |
| /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1| 3008 |
| /usr/lib/x86_64-linux-gnu/libicuuc.so.67.1| 1948 |
| /lib/x86_64-linux-gnu/libc-2.31.so| 1856 |
| /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28| 1832 |
| /lib/x86_64-linux-gnu/libm-2.31.so| 1296 |
| /usr/lib/x86_64-linux-gnu/libssl.so.1.1| 588 |
| /lib/x86_64-linux-gnu/ld-2.31.so| 172 |
| /lib/x86_64-linux-gnu/libpthread-2.31.so| 120 |
| /lib/x86_64-linux-gnu/libgcc_s.so.1| 104 |
| /lib/x86_64-linux-gnu/libresolv-2.31.so| 96 |
| /lib/x86_64-linux-gnu/libnss_files-2.31.so| 56 |
| /lib/x86_64-linux-gnu/librt-2.31.so| 40 |
| /lib/x86_64-linux-gnu/libnss_dns-2.31.so| 32 |
| /lib/x86_64-linux-gnu/libdl-2.31.so| 24 |
(2)在Debian Linux下,默认的线程堆栈大小取决于操作系统,它是
8 MiB
。(3)被“分配”的内存页面将被存储在核心转储中(只要它们没有被标记为
Not Include in Core Dump
(dd),这与它们是否被加载到物理内存中无关。(4)内存页面分配的信息,并不存储在核心转储本身。它可以通过
cat /proc/<pid>/maps
或cat /proc/<pid>/smaps
(smap包含更多细节)检索。请阅读proc man page了解详细信息。(5)内存页面,基于一个名为
memfd:doublemapper (deleted)
的文件,基本上是jit'ed代码。它被称为双Map器,因为同一个内存页面被dotnetMap两次,一次是可写的,第二次是可执行的,以避免安全问题。如果
memfd:doublemapper (deleted)
内存页面在其他时间增长,并且应用程序在运行时不使用System.Emit生成代码,则可能是委托或匿名函数无法被dotnet运行时缓存,并且必须一次又一次地jit这些代码。(6)如果smap显示匿名内存页面,占用了意外的内存,那么需要使用原生Linux工具来跟踪这些分配。
作为第一步,重要的是加载调试符号:
字符串
下一步是选择正确的调试器,我会从heaptrack开始,如果这还不足以使用strace。
型
heaptrack输出的分析可以通过它的gui应用程序或使用命令行工具
heaptrack_print -l 1 -a 0 -T 0 -p 0 -f /mnt/diagnostics/heaptrack.MyHostName_12:00:00.zst
来完成。