我有一个应用程序可以在静态字段中保存/缓存许多对象。当在应用程序的生命周期中保存了大量对象时,由于缓存变得如此之大,会引发内存不足异常。
是否有任何类或对象通知我内存已用完并且outofmemoryexception
很快就会抛出,以便我知道我需要通过删除其中一些缓存对象来释放一些内存?我正在寻找应用程序中存在内存压力的迹象,以便我可以在应用程序运行时在引发内存异常之前采取预防措施。
如果您缓存的数据太多以至于内存不足,这表明您的缓存方法存在严重问题。即使作为 32 位进程,您也有 2 GB 的地址空间。
您应该考虑使用有限缓存,用户可以在其中设置首选缓存大小,并在达到此大小时自动删除对象。您可以实施某些缓存策略来选择要从缓存中删除的对象:最旧的对象或最不常用的对象或这些对象的组合。
您还可以尝试将一些缓存映射到磁盘,并且只将最常用的对象保留在内存中。当需要一个不在内存中的对象时,您可以从磁盘反序列化它并将其放回内存中。如果某个对象在一段时间内未使用,您可以将其交换到磁盘。
我还有一个应用程序尽可能多地使用内存。根据您的具体情况,您有几个选择(都有缺点)。
最简单的方法之一是预先分配一个循环缓冲区,其中包含要缓存的类(或字节),以预先消耗所有内存。这通常不是首选,因为当你不需要时消耗一个盒子上的所有 RAM 只是简单粗鲁的。
处理大型缓存(或避免抛出内存异常)的另一种方法是在分配之前首先检查我需要的内存是否存在。这具有需要序列化内存分配的缺点。否则,可用 RAM 检查和分配之间的时间可能会用完 RAM。
这是我发现在 .NET 中执行此操作的最佳方法的示例:
//Wait for enough memory
var temp = new System.Diagnostics.PerformanceCounter("Memory", "Available MBytes");
long freeMemory = Convert.ToInt64(temp.NextValue()) * (long)1000000;
long neededMemory = (long)bytesToAllocate;
int attempts=1200; //two minutes
while (freeMemory < neededMemory)
{
//Signal that memory needs to be freed
Console.WriteLine("Waiting for enough free memory:. Free Memory:" + freeMemory + " Needed Memory(MB):" + neededMemory);
System.Threading.Thread.Sleep(100);
freeMemory = Convert.ToInt64(temp.NextValue()) * (long)1000000;
--attempts;
if (0 == attempts)
throw new OutOfMemoryException("Could not get enough free memory. File:" + Path.GetFileName(wavFileURL));
}
//Try and allocate the memory we need.
此外,一旦我进入 while 循环,我就会发出信号,表明需要释放一些内存(取决于您的应用程序)。注意:我试图通过使用 Sleep 语句来简化代码,但如果可能的话,最终你会想要某种类型的非轮询操作。
我不确定您的应用程序的细节,但是如果您是多线程的,或者运行许多不同的可执行文件,那么在将内存分配给缓存时序列化此内存检查可能会更好。如果是这种情况,我使用信号量来确保只有一个线程和/或进程可以根据需要分配 RAM。与此类似:
Semaphore namedSemaphore = new Semaphore(1, 1, "MemoryAllocationSemaphore"); //named semaphores are cross process
if (bytesToAllocate > 2000000) //if we need less than 2MB then dont bother locking.
{
if (!namedSemaphore.WaitOne((int)TimeSpan.FromMinutes(15).TotalMilliseconds))
{
throw new TimeoutException("Waited over 15 minutes for aquiring memory allocation semaphore");
}
}
然后稍后调用这个:
namedSemaphore.Release();
在 finally 块中释放信号量。
另一种选择是使用多读单写锁。让多个线程从缓存中提取数据(例如ReaderWriterLock类)。这样做的好处是,当您写入时可以检查内存压力和清理,然后将更多内容添加到缓存中,但仍然有多个线程处理数据。缺点当然是将数据放入缓存的瓶颈来自单个固定点。