dotnet-gcdump 意外的转储大小和影响
dotnet-gcdump unexpected dump size and impact
我是 运行 一个简单的 CRUD 应用程序,它是在 ubuntu 上的 docker swarm 集群中使用 ASP.NET Core 和 EF Core 3.1 构建的。我只使用托管代码。
容器指定了 10GB
内存限制。我可以检查 运行 容器并验证是否实际设置了此限制,我还看到 DOTNET_RUNNING_IN_CONTAINER
设置为 true
。当应用程序启动时,内存消耗约为 700MB
,并且会慢慢增加。一旦它达到 7GB
(我在容器通用统计信息中看到它),我开始获得 OutOfMemoryException
s 并且它在这个水平上停留了几天。所以第一个问题是
- 为什么不能达到 10 GB?
无论如何,我预计会发生内存泄漏,所以我在同一个容器中安装了一个 dotnet-gcdump 工具,所以我继续收集转储以供将来使用 dotnet-gcdump collect
进行分析。执行此命令后,我看到 运行 容器的内存消耗从 7GB
下降到 3GB
并保持在这个水平。生成的 .gcdump
文件本身大小虽然只有 ~200MB
,但没有任何可疑之处。所以接下来的问题是
- 转储的收集如何减少内存消耗?我假设它正在使用 LOH 压缩进行 GC,但它没有在文档中提及它。
- 如果该工具能够执行此操作,为什么不自动释放此内存?
- 为什么生成的转储大小只有 200 MB?
正如 gcdump 文档所解释的那样:“GC 转储是通过 在目标进程中触发 GC、打开特殊事件以及正在从事件流中重新生成对象根图。
因此,它直接回答了你的问题 2 - 它触发了完整的 GC,它可能会或可能不会压缩,但它肯定会收集 gen2。它还回答了问题 4 - 它不是“内存转储”,而是一种关于对象图(依赖项和类型名称)的特殊诊断数据,没有数据本身。
关于问题 1 和 3 - 这是 GC 不够“激进”的一个例子。当进程几乎达到容器限制并且 GC 有时无法解释它时,这是一种“生活在边缘”的问题。换句话说,它认为它有足够的space,但它没有。请注意,这是一个超级简单化的问题。在这种情况下,完整的 GC 可能不会发生或发生得太晚。我会通过 dotnet-trace
和 gc-collect
配置文件观察过程来确认这一点。
作为一个解决方案,consider setting the limit manually, by using GCHeapHardLimit
,到一些明显较小的值,如 8GB。
我是 运行 一个简单的 CRUD 应用程序,它是在 ubuntu 上的 docker swarm 集群中使用 ASP.NET Core 和 EF Core 3.1 构建的。我只使用托管代码。
容器指定了 10GB
内存限制。我可以检查 运行 容器并验证是否实际设置了此限制,我还看到 DOTNET_RUNNING_IN_CONTAINER
设置为 true
。当应用程序启动时,内存消耗约为 700MB
,并且会慢慢增加。一旦它达到 7GB
(我在容器通用统计信息中看到它),我开始获得 OutOfMemoryException
s 并且它在这个水平上停留了几天。所以第一个问题是
- 为什么不能达到 10 GB?
无论如何,我预计会发生内存泄漏,所以我在同一个容器中安装了一个 dotnet-gcdump 工具,所以我继续收集转储以供将来使用 dotnet-gcdump collect
进行分析。执行此命令后,我看到 运行 容器的内存消耗从 7GB
下降到 3GB
并保持在这个水平。生成的 .gcdump
文件本身大小虽然只有 ~200MB
,但没有任何可疑之处。所以接下来的问题是
- 转储的收集如何减少内存消耗?我假设它正在使用 LOH 压缩进行 GC,但它没有在文档中提及它。
- 如果该工具能够执行此操作,为什么不自动释放此内存?
- 为什么生成的转储大小只有 200 MB?
正如 gcdump 文档所解释的那样:“GC 转储是通过 在目标进程中触发 GC、打开特殊事件以及正在从事件流中重新生成对象根图。
因此,它直接回答了你的问题 2 - 它触发了完整的 GC,它可能会或可能不会压缩,但它肯定会收集 gen2。它还回答了问题 4 - 它不是“内存转储”,而是一种关于对象图(依赖项和类型名称)的特殊诊断数据,没有数据本身。
关于问题 1 和 3 - 这是 GC 不够“激进”的一个例子。当进程几乎达到容器限制并且 GC 有时无法解释它时,这是一种“生活在边缘”的问题。换句话说,它认为它有足够的space,但它没有。请注意,这是一个超级简单化的问题。在这种情况下,完整的 GC 可能不会发生或发生得太晚。我会通过 dotnet-trace
和 gc-collect
配置文件观察过程来确认这一点。
作为一个解决方案,consider setting the limit manually, by using GCHeapHardLimit
,到一些明显较小的值,如 8GB。