单个 .neat 进程的内存限制为约 2.5 GB
Posted
技术标签:
【中文标题】单个 .neat 进程的内存限制为约 2.5 GB【英文标题】:Memory limitted to about 2.5 GB for single .net process 【发布时间】:2019-04-11 02:19:01 【问题描述】:我正在编写在 Windows Server 2016 上运行的 .NET
应用程序,它对一大堆大文件执行 http 获取。这大大加快了下载过程,因为您可以并行下载它们。不幸的是,一旦它们被下载,需要相当长的时间才能将它们全部重新组合在一起。
有 2-4k 个文件需要合并。这将运行的服务器有足够的内存,接近800GB
。我认为使用MemoryStream
s 来存储下载的片段直到它们可以顺序写入磁盘是有意义的,但是在我得到一个之前我只能消耗大约2.5GB
的内存System.OutOfMemoryException
错误。服务器有数百 GB 可用,我不知道如何使用它们。
【问题讨论】:
ConcatenatedStream
from How do I concatenate two System.Io.Stream instances into one? 可能会满足您的需求,只要您不需要随机搜索。
并且,请确保您已编译为 x64(或任何没有“首选 32 位标志”并在 x64 上运行的 CPU)
2.它是 x64,我使用 dumpbin 来验证是否支持大地址。
这是一个简单的程序来重现我的问题。 static void Main(string[] args) MemoryStream ms1 = new MemoryStream((int)Math.Pow(1024, 3)); MemoryStream ms2 = new MemoryStream((int)Math.Pow(1024, 3)); MemoryStream ms3 = new MemoryStream((int)(Math.Pow(1024, 3)*.95)); MemoryStream ms4 = new MemoryStream((int)Math.Pow(1024, 3)); //this errors out with an out of memory error
第三个内存流很有趣。我将允许的字节总数乘以 0.95,它会起作用,如果我达到 0.96 或更高,它将不起作用。如果第三个没有,第 4 个 MemoryStream 会导致内存不足错误。
【参考方案1】:
MemoryStreams 是围绕字节数组构建的。 Arrays cannot be larger than 2GB currently.
System.Array 的当前实现对其所有内部计数器等都使用 Int32,因此理论上的最大元素数为
Int32.MaxValue
。Microsoft CLR 还规定了 2GB max-size-per-object 限制。
当您尝试将内容放在单个 MemoryStream
中时,底层数组变得太大,因此出现异常。
尝试单独存储这些片段,并在准备好后将它们直接写入FileStream
(或您使用的任何东西),而不是先尝试将它们全部连接到一个对象中。
【讨论】:
有一个应用程序设置允许创建大于 2GB 大小的数组。见docs.microsoft.com/en-us/dotnet/framework/configure-apps/… @ckuri 我相信使用它您仍然无法创建大于 2GB 的 byte 数组,因为它仍然受最大索引大小(即 UInt32 .MaxValue)。阅读备注部分:“对于字节数组,任何单个维度的最大索引为 2,147,483,591 (0x7FFFFFC7)……” 我试过了,你是对的。无法初始化长度大于 2^31 的数组。所以new long[2_000_000_000]
成功创建了一个16GB的数组,但是new byte[3_000_000_000]
抛出了OverflowException。
我尝试使用多个小于 2GB 的 MemoryStream 并将它们放入队列中。无论我使用什么大小,我都可以创建大约 2.5 GB 的空间,当我尝试创建另一个时,我会开始出现内存不足错误。
@JoshDayberry 那时你 99% 以 x86 运行它。我猜你的应用设置为 AnyCPU(首选 32 位)。这将使您的代码也作为 32 位程序集运行,请参见:***.com/a/12066861/10614791 将其显式设置为 x64(如果在 32 位系统上会彻底崩溃,则 AnyCPU 没有意义)并且它将起作用。我复制了你的问题,这样就解决了。【参考方案2】:
根据MemoryStream class 的源代码,您将无法将超过 2 GB 的数据存储到此类的一个实例中。
原因是流的最大长度设置为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 的问题很久以前就已经在微软论坛上讨论过了,并且可以参考那里的一篇关于BigArray, getting around the 2GB array size limit 的博客文章。
更新
我建议使用以下代码,它应该能够在 x64 构建上分配超过 4 GB 但在 x86 构建上会失败
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.");
【讨论】:
我在流中存储的空间不超过 2GB。然而,我使用多个流,每个块一个,来保存数据。无论我制作的块大小如何,它似乎总是在 2.5GB 的内存使用量达到峰值时才会出现内存不足错误。我将许多 MemoryStreams 存储在一个队列中。 @JoshDayberry,我明白了......你的页面文件的配置是什么?我知道如果页面文件的大小配置错误,内存分配可能会出现一些问题。我刚刚搜索了这篇文章,又找到了:Pushing the limits of windows physical memory 和Pushing the limits of windows virtual memory。最后一个可能对你有帮助... 服务器有大约 800GB 的内存,我们卡在 3 左右。页面文件利用率稳定在 0%。为什么页面文件是相关的?【参考方案3】:正如已经指出的那样,这里的主要问题是MemoryStream
的本质是由具有固定上限大小的byte[]
支持的。
已注意到使用替代Stream
实现的选项。另一种选择是研究“管道”,即新的 IO API。 “管道”基于不连续的内存,这意味着它不需要使用单个连续的缓冲区;管道库将根据需要分配多个平板,您的代码可以处理这些平板。我已经写了很多关于这个主题的文章;第 1 部分is here。第 3 部分可能最关注代码。
【讨论】:
【参考方案4】:只是为了确认我理解您的问题:您正在以多个并行块下载一个非常大的文件,并且您知道最终文件有多大?如果你不这样做,那么这确实会变得有点复杂,但它仍然可以完成。
最好的选择可能是使用MemoryMappedFile (MMF)。您要做的是通过 MMF 创建目标文件。每个线程将创建该文件的视图访问器并并行写入。最后,关闭 MMF。这实质上为您提供了 MemoryStreams 所需的行为,但 Windows 通过磁盘支持文件。这种方法的好处之一是 Windows 会在后台管理将数据存储到磁盘(刷新),因此您不必这样做,并且应该会产生出色的性能。
【讨论】:
以上是关于单个 .neat 进程的内存限制为约 2.5 GB的主要内容,如果未能解决你的问题,请参考以下文章