动态变量在内存不足时释放?

时间:2014-01-23 23:03:43

标签: c# .net variables memory-management

背景

所以前提是我有一个用属性提供数据的类。

class ExampleClass
{
   string _largeCalculatedVariable = null;
   public string largeCalculatedVariable
   {
       get
       {
           if (_largeCalculatedVariable == null)
           {
              _largeCalculatedVariable = LongRunningCalculate();
           }
           return _largeCalculatedVariable
       }
   }
}

该属性隐藏了这样一个事实:如果数据尚未生成,则数据可以即时计算数据但如果之前已经预先计算或计算过,则会返回缓存值。

问题

问题是可能会生成一大堆ExampleClass,如果我访问largeCalculatedVariable就足够了,我可能会耗尽内存。既然我可以随时重新计算该值,是否可以告诉.NET在需要内存时放弃_largeCalculatedVariable

注意:我觉得我可能在这里使用了错误的设计模式。

3 个答案:

答案 0 :(得分:7)

如果您使用的是.NET 4.0或更新版本,您可以使用MemoryCache类,您可以将其设置为有应该存储多少数据的规则,如果它已经命中,它将自行清除以腾出空间这是限制。

class ExampleClass
{
   //This is a static constructor;
   static ExampleClass()
   {
       var settings = new NameValueCollection();
       settings.Add("PhysicalMemoryLimitPercentage", "75");

       _cache = new MemoryCache("ExampleClassCache", settings);
   }

   private static MemoryCache _cache;

   public ExampleClass()
   {
      _cacheKey = Guid.NewGuid().ToString();
   }

   private readonly string _cacheKey;

   public string largeCalculatedVariable
   {
       get
       {
            var record = _cache.Get(_cacheKey) as string;

            //If record was null that means the item was not in the cache.
            if(record == null)
            {
                record = LongRunningCalculate();
                _cache.Add(_cacheKey, record, new CacheItemPolicy(), null);
            }

           return record;
       }
   }
}

如果你想要,你也可以让ExampleClass从缓存中取出物品,以便在处理对象时释放空间,实现起来并不多。

class ExampleClass : IDisposable
{
    //...Everything else is the same from the previous code example.

    public void Dispose()
    {
        Dispose(true)
        GC.SupressFinialize(this);
    }

    bool _disposed;

    protected virtual void Dispose(bool disposing)
    {
        if(!_disposed)
        {
            if(disposing)
            {
                //Nothing to do here, we want to remove from the cache if Dispose or the finalizer is called.
                //But you may have IDisposeable objects in your real class, they should be disposed in here.
            }

            if(_cacheKey != null)
                _cache.Remove(_cacheKey, null);

            _disposed = true;
        }
    }

    ~ExampleClass()
    {
        Dispose(false);
    }
}

答案 1 :(得分:3)

您可以使用WeakReference。它允许保留对某些数据的引用,但告诉Garbage Collector在需要时声明该引用,这正是您想要的。

  

弱引用允许垃圾收集器在仍允许应用程序访问对象的同时收集对象。如果您需要该对象,您仍然可以获得它的强引用并防止它被收集。

_data = new WeakReference("Hamster");

return _data.IsAlive ? data.Target : CreateData();

答案 2 :(得分:1)

我想补充一点,除了使用 WeakReference MemoryCache 的优秀答案(后者可能是您更好的选择)使用类型Lazy<>。 此类型已经解决了您可能遇到的任何并发问题。 有人可能会想:'我没有多线程,我为什么要关心?'

  1. 使用Lazy<>
  2. 时,您的代码并不复杂
  3. 可扩展性:一旦您想利用该应用程序进行多线程,它就已经准备好了。
  4. 从.Net 4.0开始我总是实现像这样的延迟加载属性

    string Lazy<string> _largeCalculatedVariable = null;//No need to set it to null but whatever you prefer
    public string largeCalculatedVariable
       {
           get
           {
               if (_largeCalculatedVariable == null)
               {
                  _largeCalculatedVariable = new Lazy<string>(()=>
                  {
                     _largeCalculatedVariable = LongRunningCalculate();
                  }
               }
               return _largeCalculatedVariable.Value
           }
       }