我在 C# 的帮助下测试了 Docker 容器的不同限制。出于测试目的,我编写了一个简单的程序来生成不同类型的负载(CPU 密集型工作、内存分配、网络请求等)。
对于具有许多内存分配的场景,我编写以下代码:
try {
var chunks = new byte[10][];
for (var chunkId = 0; chunkId < 10; chunkId++) {
Console.WriteLine($"Chunk: {chunkId}");
var chunk = new byte[100 * 1024 * 1024];
for (var i = 0; i < chunk.Length; i++)
chunk[i] = (byte) (i % 256);
chunks[chunkId] = chunk;
}
}
catch (Exception) { // To capture OutOfMemoryException and pause the process
Thread.CurrentThread.Join();
}
Run Code Online (Sandbox Code Playgroud)
此代码分配了 10 个 100MB 连续内存段(总共 1GB),但当我在具有内存限制的容器内运行它时,神奇的事情发生了:
$> docker run --memory=1g --memory-swap=1g <workload-image>
Run Code Online (Sandbox Code Playgroud)
在 RAM 限制为 1GB 的容器中,我的应用程序只能分配 6-7 个 100MB 的块,并且对新字节数组的请求失败,并显示OutOfMemoryException.
起初,我认为 CLR 会消耗某些内部结构的内存,但在深入研究内存转储数小时后,我在 .NET 堆中没有发现任何可疑的大对象。
最后,我设置了以下简单的实验。我在具有 1GB RAM 限制的 Docker 容器中运行我的应用程序,OutOfMemoryException在我的语句中处理catch并暂停进程后,我运行程序的另一个副本,它突然可以分配 3 个额外的内存块 (300MB)。
我无法解释为什么我的应用程序无法分配具有 1GB RAM 限制的所有块(或至少 9/10 块)。有人能解释一下 .NET 的这种神秘行为吗?
在 Ubuntu 18.10 中使用 Docker Desktop(Windows 10,预分配 5GB RAM 内存)和 docker 19.03.11 重现了该问题。我的项目目标netcoreapp3.1和最终图像基于mcr.microsoft.com/dotnet/core/sdk:3.1.300. 我将我的镜像推送到 Docker hub 注册表中,如果这对问题调查有帮助的话。
UPD:我用 C++ 编写了一个简单的 C# 应用程序模拟,它在 1GB 限制下成功运行。所以,我认为这与 .NET CLR 紧密相关。
此外,根据顶部(RES列),容器应用程序消耗了 724 MB:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 5749076 741512 19932 S 0.0 9.3 0:01.05 dotnet
Run Code Online (Sandbox Code Playgroud)
HeapHardLimit 默认为容器内存限制的 75%,请参阅https://learn.microsoft.com/en-us/dotnet/core/run-time-config/garbage-collector#heap-limit
因此,在 1GB 容器限制的情况下,您应该能够在单个 dotnet 进程中分配 7x100MB 块。
| 归档时间: |
|
| 查看次数: |
8020 次 |
| 最近记录: |