检测即将耗尽的内存(获取“免费物理内存”的数量)

我正在将图像从高FPS相机传输到内存缓冲区(列表)中,并且由于这些图像非常大,因此计算机的内存耗尽非常快。

我想做的是在应用程序内存不足之前停止传输一段时间。 在我的测试过程中,我发现它与“Free Physical Memory”指标一致,接近于零。

现在的问题是我找不到实际以编程方式获取此值的方法; 在XP中,它甚至没有显示在任何地方(仅在Vista / 7任务管理器中)。

替代文字

我已经尝试了所有可以找到的方法(WMI,性能计数器,MemoryStatus,……),但我从那些方面得到的只是“可用物理内存”,当然不一样。

有任何想法吗?

更新不幸的是,我需要数据在内存中(是的,我知道我不能保证它将在物理内存中,但仍然),因为数据是实时流式传输的,我需要在内存中预览它它被存储在那里。

我迟到了,但你考虑过使用System.Runtime.MemoryFailPoint类吗? 它做了很多事情来确保请求的分配成功,如果失败则抛出InsufficientMemoryException; 你可以抓住这个并停止转移。 您可以预测传入帧的平均大小,并尝试分配3或4个帧,然后在发生故障时停止采集。 也许是这样的?

const int AverageFrameSize = 10 * 1024 * 1024 // 10MB void Image_OnAcquired(...) { try { var memoryCheck = new MemoryFailPoint(AverageFrameSize * 3); } catch (InsufficientMemoryException ex) { _camera.StopAcquisition(); StartWaitingForFreeMemory(); return; } // if you get here, there's enough memory for at least a few // more frames } 

我怀疑它是100%万无一失,但它是一个开始。 由于其他答案中解释的原因,它肯定比性能计数器更可靠。

相关性不是因果关系。 即使物理内存负载仍然空闲,您也可以“耗尽内存”。 物理记忆几乎肯定无关紧要; 你可能用完的是地址空间

人们倾向于将“记忆”视为消耗芯片上的空间,但十多年来情况并非如此。 现代操作系统中的内存通常被认为是一个大型磁盘文件,其上面有一个大硬件缓存以加速它。 物理内存只是基于磁盘的内存的性能优化

如果你的物理内存不足,那么你的表现会很糟糕。 但是,稀缺的资源实际上是你正在耗尽的地址空间 。 一个大的列表必须有一个大的连续的地址空间块,并且可能没有任何块大到你想要的大小。

不要那样做。 下拉一个合理大小的块,将其转储到磁盘,然后根据需要处理磁盘上的文件。

您无法在Vista / 7中单独使用空闲内存计数器作为指南,因为它可能始终接近于零。 原因是Vista / 7的superfetch使用可用内存来缓存它认为你可能会使用的磁盘中的东西。

Linky: http : //www.codinghorror.com/blog/2006/09/why-does-vista-use-all-my-memory.html

此外,如果您正在运行32位C#进程,那么每个进程的内存限制为2GB(实际上在事情变得不稳定之前更像是1.5GB)所以即使你的盒子显示你有大量的可用内存,你也会当您的进程达到2GB限制时仍会出现内存不足exception。

正如Tergiver上面评论的那样,真正的解决方案是避免将所有文件保存在内存中,而是根据需要将图像的比特交换进出内存。

感谢所有的答案。

我一直在考虑它并得出结论,做我最初想要的事情是非常困难的(如果不是不可能的话),那就是以某种方式检测应用程序何时耗尽内存。

所有答案似乎指向相同的方向(以某种方式保持数据不在内存中),但不幸的是我不能去那里,因为我真的“需要”数据留在内存中(如果可能的话,物理)。

由于我必须做出妥协,我决定为用户创建一个设置来决定捕获数据的内存使用限制。 它至少易于实施。

想要添加我自己的答案,因为OwenP的其他好的答案在使用System.Runtime.MemoryFailPoint的方式上有两个重要的错误。

第一个错误是一个非常简单的修复:构造函数签名是public MemoryFailPoint(int sizeInMegabytes)因此AverageFrameSize参数应该以兆字节为单位,而不是字节。 还要注意以下大小:

MemoryFailPoint的粒度为16 MB。 任何小于16 MB的值都被视为16 MB,其他值被视为16 MB的下一个最大倍数。

第二个错误是MemoryFailPoint实例必须保持活动状态,直到你想要使用的内存被分配, 然后处理掉!

这可能有点难以修复,可能需要根据OP的实际代码看起来进行设计更改。

您必须以这种方式处理它的原因是MemoryFailPoint类保留了由其构造函数创建的进程范围内存保留记录。 这样做是为了确保如果两个线程在大致相同的时间执行内存检查,除非有足够的内存来满足两个线程的需求,否则它们都不会成功。 (否则, MemoryFailPoint类在multithreading应用程序中将毫无用处!)

调用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页面上找到有关数组的内容 。

第二种方法(调用者负责MemoryFailPoint实例)可能看起来像这样:

 const int AverageFrameSizeInMegabytes = 10; // 10MB ///  /// Tries to create a MemoryFailPoint instance for enough megabytes to /// hold as many images as specified by . ///  ///  /// A MemoryFailPoint instance if the requested amount of memory was /// available (at the time of this call), otherwise null. ///  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.

猜测被删除的单词是什么并不难。


这是一篇有趣的文章,关于如何克服数组的2GB限制 。

此外,如果您需要分配非常大的数据结构,您需要了解可以在app-config中设置的


值得一提的是,这与OP真正想要的物理内存没有任何关系。 事实上, MemoryFailPoint在放弃并报告失败之前会尝试做的事情之一就是增加页面文件的大小。 但是,如果使用得当,它将避免出现OutOfMemoryException ,这至少是OP想要的一半。

如果你真的想把数据强制进入物理内存那么,据我所知,你必须使用AllocateUserPhysicalPages本地化 ,这不是世界上最容易出错的东西,需要适当的权限和几乎肯定是矫枉过正。 操作系统真的不想被告知如何管理内存,所以它不容易这么做...

获取OutOfMemoryException仅意味着无法遵守当前的内存分配。 它并不一定意味着系统甚至进程内存不足。 想象一下一个hello world类型的应用程序,它首先分配一个2 GB的内存块。 在32位系统上,尽管该进程此时尚未真正分配任何重要内存,但很可能会触发exception。

OutOfMemoryExceptions的常见来源是没有足够的连续内存可用。 即大量内存可用,但没有大块足以满足当前请求。 换句话说,通过观察空闲内存计数器来避免OOM是不可行的。