gpt4 book ai didi

c# - 使用非托管内存时出现奇怪的内存使用情况

转载 作者:太空宇宙 更新时间:2023-11-03 20:49:21 40 4
gpt4 key购买 nike

我们的应用程序需要通过互操作调用某个非托管的第 3 方库。该应用程序线程密集,每分钟调用此函数数千次,这就是我们不泄漏内存至关重要的原因。

在我们的服务器上运行该应用程序时,我注意到该应用程序的内存占用量在增加,经过数周的导航问题后,我最终将其缩小到这个非常奇怪的问题:

我在使用非托管代码时观察到一个非常奇怪的行为,在分配和取消分配非托管指针之后,内存似乎没有按预期下降。

这是重现问题的最少代码:

Platform: x64
Build: Release (optimised)
Windows Server 2012
64GB memory
Dual Xeon processors with 64 logical cores
<gcServer enabled="true />

代码

class Program
{
static void Main()
{
// LargeFile.abc is representing a byte[] of ~300kb
byte[] largeArray = File.ReadAllBytes(Path.Combine(Directory.GetCurrentDirectory(), "Data", "LargeFile.abc"));
TestMemoryAllocation(largeArray);

Console.WriteLine("Done");
Console.ReadLine();
}

private static void TestMemoryAllocation(byte[] byteArray)
{
Parallel.For(0,
1000000,
i =>
{
FromStream(byteArray, ptr =>
{
// simulate some work with the unmanaged pointer
Thread.Sleep(50);
});
});
}

private static void FromStream(byte[] src, Action<IntPtr> func)
{
IntPtr unmanagedArray = IntPtr.Zero;
try
{
// allocate the memory on the unmanaged heap
unmanagedArray = Marshal.AllocHGlobal(src.Length);

// do something with this unmanaged pointer
func(unmanagedArray);
}
finally
{
// free the space
Marshal.FreeHGlobal(unmanagedArray);
}
}
}

通常,我的预期是,根据内核数量和线程调度,内存会上下波动,但最终会稳定下来并徘徊在最小值和最大值之间。相反,这是我观察到的:

ProcessExplorer/TaskManager(这是一个 Windows 服务器)中的私有(private)工作字节不断增加,内存分析器(使用 ANTS 和 JetBrains dotMemory)都报告运行时内存消耗增加。

这是运行应用程序直到完成时 dotMemory 的屏幕截图: enter image description here

红色线是实际发生的情况(当运行上面的代码示例时),而蓝色线是我期望看起来的样子。

我也尝试过添加/删除 GC 压力 (GC.Add/RemoveMemoryPressure),这没有帮助,但这是可以理解的,因为这里没有发生 GC。我在 Window 10 桌面上尝试了同样的事情,但观察到了同样的行为。

这是怎么回事?我们的服务器按进程控制内存使用,如果内存超过一定水平,它会终止并重新启动进程,这意味着我们必须小心我们的内存使用并需要能够控制它。

更新:使用 Thread.Sleep(0) 而不是 Thread.Sleep(50) 的图形:

enter image description here非常感谢,

最佳答案

好吧,对你的测试代码做一些内存分析(请注意,我将分配增加到 100 倍以更快地到达那里:

  1. GC 从未发生过。这并不奇怪,因为你并没有真正分配太多托管内存——一分钟后(当我遇到内存不足崩溃时),堆已经增长了大约 10 kiB,没有理由让 GC 启动。
  2. 我有 40 个工作线程(在 4 核 CPU 上)。这也不足为奇,但可能是您遇到问题的原因。当您使用的同步代码没有完成足够的 CPU 工作时,工作线程池将增加线程池以适应更多的并发工作负载。它不关心你使用了多少内存。这意味着,如果您的线程完成速度不快于新线程的启动速度,那么您的内存使用量将不断增加,直到进程崩溃。

随着内存的增加,检查服务器上运行的线程数。使用量的跳跃似乎很好地跟随了新工作线程的创建。请注意,代码中只有一个 Thread.Sleep(50),它限制了您获得的最大线程数;我预计您的实际工作量可能远不止于此。

因此,在我看来,这并不是真正的内存泄漏问题 - 这是一个节流问题。您可以通过一个系统来限制您允许用于此特定问题的线程数来解决此问题。在您的测试应用程序中,这很简单:

Parallel.For(0, 1000000, new ParallelOptions { MaxDegreeOfParallelism = 10 },
i =>
{
FromStream(byteArray, ptr =>
{
// simulate some work with the unmanaged pointer
Thread.Sleep(50);
});
}
);

瞧 - 您的内存使用量达到峰值并保持稳定。调整并行度以适应您实际可以并行执行的工作量以及可用内存量。

关于c# - 使用非托管内存时出现奇怪的内存使用情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57070194/

40 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com