【问题标题】:Memory pressure informer/counter/statistics内存压力通知器/计数器/统计
【发布时间】:2013-06-12 02:29:02
【问题描述】:

我有一个在静态字段中保存/缓存许多对象的应用程序。当在应用程序的生命周期中保存了大量对象时,由于缓存增长如此之大,会引发内存不足的异常。

是否有任何类或对象通知我内存已用完,并且很快就会有outofmemoryexception 抛出,以便我知道我需要通过删除其中一些缓存对象来释放一些内存?我正在寻找应用程序中存在内存压力的迹象,以便在引发内存异常之前在应用程序运行时采取预防措施。

【问题讨论】:

标签: c# wcf memory


【解决方案1】:

如果您缓存的数据过多以至于内存不足,则表明您的缓存方法存在严重问题。即使是 32 位进程,您也有 2 GB 的地址空间。

您应该考虑使用有限缓存,用户可以在其中设置首选缓存大小,并在达到此大小时自动删除对象。您可以实施某些缓存策略来选择从缓存中删除哪些对象:最旧的对象或最不常用的对象或这些对象的组合。

您也可以尝试将一些缓存映射到磁盘,并且只将最常用的对象保留在内存中。当需要一个不在内存中的对象时,您可以从磁盘反序列化它并将其放回内存中。如果某个对象在一段时间内未使用,您可以将其交换到磁盘。

【讨论】:

    【解决方案2】:

    我还有一个应用程序尽可能多地使用内存。根据您的具体情况,您有几个选择(都有缺点)。

    其中一个最简单的方法是预先分配一个循环缓冲区,其中包含要缓存的类(或字节),以预先消耗所有内存。这通常不是首选,因为当你不需要时消耗一个盒子上的所有 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 类)。这样做的好处是,当您写入时可以检查内存压力和清理,然后将更多内容添加到缓存中,但仍然有多个线程处理数据。缺点当然是将数据放入缓存的瓶颈来自单个固定点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-20
      • 1970-01-01
      • 2015-08-30
      • 1970-01-01
      • 2016-06-09
      • 2014-02-14
      • 1970-01-01
      • 2011-07-18
      相关资源
      最近更新 更多