.NET中奇怪的数组内存pipe理

我写了一个WPF应用程序,针对.NET 4.5.2 64位。 它运行在Windows 8.1 Professional 64位上。

该应用程序用于同时录制多个video。 由于video编码器的速度在运行过程中可能会有很大的不同,因此我在内存中制作了一些非常大的字节数组(总共6 GiB),这足以让我缓冲40秒的video,并让编码发生在一个单独的线程上,所以我不冒险被编码阻挡,并错过来自相机的传入帧。

我已经读了一个地方(字节)数组在MySQL中懒惰地分配 – 即只写byte[] foo = new byte[1024*1024]; 并不意味着1 MiB立即分配; 内存实际上没有分配,直到第一次访问。 虽然我找不到我在哪里读了。 无论如何,知道这一点,我一定要在我的数组上调用Array.Clear() ,这迫使他们立即分配。 我还需要确保缓冲区始终保留在RAM中,所以我已经完全禁用了页面文件。

但由于某种原因,似乎.NET运行时或Windows仍然在做一些疯狂的事情:就在初始化我的字节数组并在所有这些数组上调用Array.Clear()之后,我可以在任务pipe理器中看到7.4 GiB在使用。 它坐在那里, 直到我按下“logging”button,此时我开始有实际的数据填充缓冲区。 而奇怪的是,使用中的内存开始下降到大约3.6吉比特,然后慢慢爬回7.4吉比特。 我的应用程序从来没有放弃和重新分配数组。 另外请记住,因为我已经禁用了页面文件,所以Windows没有地址可以分页。

到底是怎么回事? 我应该如何解释这个怪事?

我在任务管理器中看到的

到底是怎么回事? 我应该如何解释这个怪事?

这并不奇怪,这只是垃圾收集的工作原理。

当你使用Array.Clear ,你只是将这些字节重置为0(谢谢@Martin)。 这并不意味着垃圾收集将在同一时间发生 。 如果数组仍然被引用,这甚至不会使它可以被收集。 GC的运行时间是不确定的。

你所看到的实际上是有道理的,因为你看到一旦你按下“Record”,并且运行时需要分配更多的资源来使用,它首先调用一个集合,然后开始缓慢地增加,因为这些字节被你的应用程序。

正如其他人在评论中所说的那样,如果您需要在您的应用程序中管理的内存上使用细粒度的粒度,则最好使用较低级别的语言,这样可以控制(尽可能地)分配和释放内存。

不,.NET数组不会被懒惰地分配。 然而,操作系统有一个小技巧 – 当你在内存中分配一个预先归零的页面(而.NET总是分配预置的页面)时,它实际上并不会给你物理内存 – 相反,所有的页面都指向单)零页。 在你真的把数据写到你的空数组之前,所有存储数组数据的页面都是这个页面。

这意味着虽然进程的虚拟内存总是您所期望的(例如,1024 * 1024字节数组的兆字节),但物理RAM使用率却不是。 这与操作系统决定将内存分页到页面文件时相似 – 你无法控制,而且你真的不应该这样做。

在实践中,这不应该为你的申请做任何改变。 您仍然拥有连续的虚拟内存空间,只要您真正需要,操作系统将为您提供真实的内存。 除非你的系统在内存压力下,否则你会立即得到真正的内存(可能需要清零)。