想添加我自己的答案,因为原本不错的 answer by OwenP 在使用 System.Runtime.MemoryFailPoint 的方式上有两个重要错误。
第一个错误很容易修复:构造函数签名是public MemoryFailPoint(int sizeInMegabytes),所以AverageFrameSize 参数应该以兆字节为单位,而不是字节。还要注意以下关于尺寸的内容:
MemoryFailPoint 以 16 MB 的粒度运行。任何小于 16 MB 的值都被视为 16 MB,其他值被视为 16 MB 的下一个最大倍数。
第二个错误是MemoryFailPoint 实例必须保持活动状态,直到您希望使用的内存被分配,然后释放!
这可能有点难以修复,并且可能需要根据 OP 的实际代码进行设计更改。
您必须以这种方式处理它的原因是MemoryFailPoint 类保留了从其构造函数进行的内存预留的进程范围记录。这样做是为了确保如果两个线程大致同时执行内存检查,除非有足够的内存来满足两个线程的需求,否则它们不会都成功。 (否则MemoryFailPoint 类在多线程应用程序中将毫无用处!)
构造函数“保留”的内存在调用Dispose() 时是未保留的。因此,线程应该在它分配了所需内存之后尽快处理MemoryFailPoint-instance,但不是在此之前。
(“尽快”部分是首选但不是关键。延迟处置可能会导致其他内存检查不必要地失败,但至少你会在保守方面犯错。)
上述要求是需要更改代码设计的。检查内存的方法也必须执行分配,或者必须将MemoryFailPoint 实例传递给调用者,这使得调用者有责任在正确的时间处理它。 (后者是 MSDN 上的示例代码所做的。)
使用第一种方法(和固定的缓冲区大小)可能看起来像这样:
const int FrameSizeInMegabytes = 10; // 10MB (perhaps more is needed?)
const int FrameSizeInBytes = FrameSizeInMegabytes << 20;
// shifting by 20 is the same as multiplying with 1024 * 1024.
bool TryCreateImageBuffer(int numberOfImages, out byte[,] imageBuffer)
{
// check that it is theoretically possible to allocate the array.
if (numberOfImages < 0 || numberOfImages > 0x7FFFFFC7)
throw new ArgumentOutOfRangeException("numberOfImages",
"Outside allowed range: 0 <= numberOfImages <= 0x7FFFFFC7");
// check that we have enough memory to allocate the array.
MemoryFailPoint memoryReservation = null;
try
{
memoryReservation =
new MemoryFailPoint(FrameSizeInMegabytes * numberOfImages);
}
catch (InsufficientMemoryException ex)
{
imageBuffer = null;
return false;
}
// if you get here, there's likely to be enough memory
// available to create the buffer. Normally we can't be
// 100% sure because another thread might allocate memory
// without first reserving it with MemoryFailPoint in
// which case you have a race condition for the allocate.
// Because of this the allocation should be done as soon
// as possible - the longer we wait the higher the risk.
imageBuffer = new byte[numberOfImages, FrameSizeInBytes];
//Now that we have allocated the memory we can go ahead and call dispose
memoryReservation.Dispose();
return true;
}
0x7FFFFFC7 是单字节类型数组的任何维度中允许的最大索引器,可以在MSDN page about arrays 上找到。
第二种方法(调用者负责MemoryFailPoint 实例)可能如下所示:
const int AverageFrameSizeInMegabytes = 10; // 10MB
/// <summary>
/// Tries to create a MemoryFailPoint instance for enough megabytes to
/// hold as many images as specified by <paramref name="numberOfImages"/>.
/// </summary>
/// <returns>
/// A MemoryFailPoint instance if the requested amount of memory was
/// available (at the time of this call), otherwise null.
/// </returns>
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 没有强保证的原因之一。
还请注意,“保留”内存只是作为数量进行跟踪。由于它不是对特定内存段的实际保留,这进一步削弱了保证,如参考来源中的以下令人沮丧的评论所示:
// Note that multiple threads can still ---- on our free chunk of address space, which can't be easily solved.
不难猜出被删减的词是什么。
这是一篇关于how to overcome the 2GB limit on arrays的有趣文章。
此外,如果您需要分配非常大的数据结构,您需要了解<gcAllowVeryLargeObjects>,您可以在应用配置中设置。
这与 OP 真正想要的物理内存没有任何关系,这毫无价值。事实上,MemoryFailPoint 在放弃并报告失败之前会尝试做的一件事是增加页面文件的大小。但如果使用得当,它将做得非常好,避免获得OutOfMemoryException,这至少是 OP 想要的一半。
如果您真的想将数据强制存储到物理内存中,那么据我所知,您必须使用 AllocateUserPhysicalPages 进行原生处理,这不是世界上最简单的事情可能出错的事情,需要适当的权限,几乎可以肯定是矫枉过正。操作系统真的不喜欢被告知如何管理内存,所以这样做并不容易......