我想添加自己的答案,因为除了 OwenP的答案之外,在使用System.Runtime.MemoryFailPoint时有两个重要错误。
第一个错误非常简单,可以轻松解决:构造函数签名是public MemoryFailPoint(int sizeInMegabytes)
,因此AverageFrameSize
参数应该以兆字节为单位,而不是字节。还要注意以下关于大小的内容:
MemoryFailPoint以16 MB的粒度运行。任何小于16 MB的值都将视为16 MB,其他值都将视为下一个最大的16 MB倍数。
第二个错误是必须保持MemoryFailPoint
实例存活,直到分配了您希望使用的内存,然后才能进行处理!
这可能有点难以解决,并且可能需要根据OP实际代码进行设计更改。
您需要以这种方式处理它的原因是,
MemoryFailPoint
类会从其构造函数中保留内存分配的进程级记录。这样做是为了确保如果两个线程在大约相同的时间内执行内存检查,则它们不会同时成功,除非有足够的内存来满足两个线程的需求。(否则,
MemoryFailPoint
类在多线程应用程序中将无用!)
构造函数保留的内存在调用
Dispose()
时取消保留。因此,线程应该尽快在分配所需内存后释放
MemoryFailPoint
实例,但不要在此之前释放。
(“尽快”部分是首选但不是关键。延迟处理可能会导致其他内存检查不必要地失败,但至少可以保守处理。)
上述要求需要修改代码设计。检查内存的方法必须执行分配,或者它必须将
MemoryFailPoint
实例传递给调用者,这使得调用者有责任在正确的时间处置它。(这是MSDN上示例代码所做的事情。)
使用第一种方法(和固定的缓冲区大小)可能看起来像这样:
const int FrameSizeInMegabytes = 10;
const int FrameSizeInBytes = FrameSizeInMegabytes << 20;
bool TryCreateImageBuffer(int numberOfImages, out byte[,] imageBuffer)
{
if (numberOfImages < 0 || numberOfImages > 0x7FFFFFC7)
throw new ArgumentOutOfRangeException("numberOfImages",
"Outside allowed range: 0 <= numberOfImages <= 0x7FFFFFC7");
MemoryFailPoint memoryReservation = null;
try
{
memoryReservation =
new MemoryFailPoint(FrameSizeInMegabytes * numberOfImages);
}
catch (InsufficientMemoryException ex)
{
imageBuffer = null;
return false;
}
imageBuffer = new byte[numberOfImages, FrameSizeInBytes];
memoryReservation.Dispose();
return true;
}
0x7FFFFFC7
是任何单字节类型的数组在任何维度上允许的最大索引器,可以在MSDN有关数组的页面中找到。
第二种方法(调用方负责MemoryFailPoint
实例)可能如下所示:
const int AverageFrameSizeInMegabytes = 10;
MemoryFailPoint GetMemoryFailPointFor(int numberOfImages)
{
MemoryFailPoint memoryReservation = null;
try
{
memoryReservation =
new MemoryFailPoint(AverageFrameSizeInMegabytes * numberOfImages);
}
catch (InsufficientMemoryException ex)
{
return null;
}
return memoryReservation;
}
这看起来简单得多(并且更灵活),但现在需要调用者处理
MemoryFailPoint
实例,并在正确的时间点处处理它。(由于我没有想出一个好的和描述性的方法名称,所以添加了一些强制性文档。)
重要提示:在此上下文中,“保留”是什么意思
内存不是“保留”的意思是它被保证可用(对于调用线程)。它只意味着当线程使用
MemoryFailPoint
检查内存时,假设成功,它会将其内存大小添加到进程范围(静态)的“保留”量中,而
MemoryFailPoint
类会跟踪此量。此保留将导致任何其他对
MemoryFailPoint
的调用(例如来自其他线程)将把总剩余内存量视为实际减去当前进程范围(静态)的“保留”量。(当
MemoryFailPoint
实例被处理时,它们会从保留总量中扣除其数量。)然而,实际的内存分配系统本身并不知道或关心这种所谓的“保留”,这也是
MemoryFailPoint
没有强烈保证的原因之一。
请注意,所谓的“保留”内存只是作为一定数量进行跟踪。由于它不是对特定内存段的实际保留,这进一步削弱了保证,正如在参考源代码中发现的以下沮丧评论所说明的那样:“//请注意,多个线程仍然可以在我们的空闲地址空间上竞争,这很难解决。” 不难猜出被审查的词是什么。
这是一篇关于
如何克服数组的2GB限制的有趣文章。
此外,如果您需要分配非常大的数据结构,您需要了解
<gcAllowVeryLargeObjects>
,您可以在应用配置中设置它。
值得注意的是,这与仅涉及物理内存没有任何关系,正如 OP 真正想要的那样。事实上,在放弃并报告失败之前,
MemoryFailPoint
将尝试做的其中一件事情是增加页面文件的大小。但如果使用正确,它将非常好地避免出现
OutOfMemoryException
,这至少是 OP 想要的一半。
如果你真的想要将数据强制写入物理内存,那么据我所知,你必须使用
AllocateUserPhysicalPages 进行本地操作。这并不是世界上最简单的事情,会有大量可能出错的事情,并且需要适当的权限,几乎肯定是过度的。操作系统不喜欢被告知如何管理内存,因此不容易做到这一点……