单个 .neat 进程的内存限制为约 2.5 GB

Memory limitted to about 2.5 GB for single .net process

我在 Windows Server 2016 上编写 .NET 应用程序 运行ning,它在一堆大文件上执行 http 获取。这大大加快了下载过程,因为您可以并行下载它们。不幸的是,一旦它们被下载,需要相当长的时间才能将它们重新组合在一起。

有2-4k 个文件需要合并。 运行 所在的服务器内存充足,接近 800GB。我认为使用 MemoryStreams 存储下载的片段直到它们可以顺序写入磁盘是有意义的,但是 我只能消耗大约 2.5GB 的内存,然后才出现 System.OutOfMemoryException 错误。服务器有数百 GB 可用,我不知道如何使用它们。

MemoryStreams 是围绕字节数组构建的。 Arrays cannot be larger than 2GB currently.

The current implementation of System.Array uses Int32 for all its internal counters etc, so the theoretical maximum number of elements is Int32.MaxValue.

There's also a 2GB max-size-per-object limit imposed by the Microsoft CLR.

当您尝试将内容放在单个 MemoryStream 中时,基础数组变得太大,因此出现异常。

尝试单独存储片段,并在准备好后将它们直接写入FileStream(或任何你使用的),而不是首先尝试将它们全部连接成1 个对象。

只是为了确认我理解您的问题:您正在以多个并行块的形式下载一个非常大的文件,您知道最终文件有多大吗?如果你不这样做,那么这确实会变得有点复杂,但它仍然可以完成。

最好的选择可能是使用 MemoryMappedFile (MMF)。您要做的是通过 MMF 创建目标文件。每个线程将创建一个对该文件的视图访问器并并行写入它。最后,关闭 MMF。这实质上为您提供了 MemoryStreams 所需的行为,但 Windows 按磁盘备份文件。这种方法的好处之一是 Windows 管理在后台将数据存储到磁盘(刷新),因此您不必这样做,并且应该会产生出色的性能。

根据 MemoryStream class 的源代码,您将无法将超过 2 GB 的数据存储到此 class 的一个实例中。 这样做的原因是流的最大长度设置为 Int32.MaxValue,并且 array 的最大索引设置为 0x0x7FFFFFC7,即十进制 2.147.783.591(= 2 GB) .

片段内存流

private const int MemStreamMaxLength = Int32.MaxValue;

片段数组

// We impose limits on maximum array lenght in each dimension to allow efficient 
// implementation of advanced range check elimination in future.
// Keep in sync with vm\gcscan.cpp and HashHelpers.MaxPrimeArrayLength.
// The constants are defined in this method: inline SIZE_T MaxArrayLength(SIZE_T componentSize) from gcscan
// We have different max sizes for arrays with elements of size 1 for backwards compatibility
internal const int MaxArrayLength = 0X7FEFFFFF;
internal const int MaxByteArrayLength = 0x7FFFFFC7;

问题More than 2GB of managed memory has already been discussed long time ago on the microsoft forum and has a reference to a blog article about BigArray, getting around the 2GB array size limit有。

更新

我建议使用以下代码,它应该能够在 x64 构建上分配超过 4 GB 但在 x86 构建上将失败 < 4 GB

private static void Main(string[] args)
{
    List<byte[]> data = new List<byte[]>();
    Random random = new Random();

    while (true)
    {
        try
        {
            var tmpArray = new byte[1024 * 1024];
            random.NextBytes(tmpArray);
            data.Add(tmpArray);
            Console.WriteLine($"{data.Count} MB allocated");
        }
        catch
        {
            Console.WriteLine("Further allocation failed.");
        }
    }
}

正如已经指出的那样,这里的主要问题是 MemoryStream 由具有固定上限的 byte[] 支持的性质。

已注意到使用替代 Stream 实现的选项。另一种选择是研究 "pipelines",新的 IO API。 "pipeline" 基于不连续内存,这意味着不需要使用单个连续缓冲区;管道库将根据需要分配多个板,您的代码可以处理这些板。我已经写了很多关于这个主题的文章;第 1 部分 is here。第 3 部分可能具有最多的代码重点。