访问 MemoryCache 会创建副本吗?

Posted

技术标签:

【中文标题】访问 MemoryCache 会创建副本吗?【英文标题】:Does accessing MemoryCache create a copy? 【发布时间】:2015-02-08 01:25:48 【问题描述】:

我有这样的缓存服务:

public interface ICacheService 
    T Get<T>(string cacheID, Func<T> getItemCallback, int cacheMinutes = 5) where T : class;


public class MemoryCacheService : ICacheService 
    public T Get<T>(string cacheId, Func<T> getItemCallback, int cacheMinutes = 5) where T : class 
        T item = MemoryCache.Default.Get(cacheId) as T;
        if (item == null) 
            item = getItemCallback();
            MemoryCache.Default.Add(cacheId, item,
                new CacheItemPolicy AbsoluteExpiration = DateTime.Now.AddMinutes(cacheMinutes));
        
        return item;
    

并像这样检索:

var result = _cache.Get("mylist", () => _database.Fetch<MyList>().AsQueryable(), 600);

该列表很大,并且在每次击键预先输入下拉列表中经常访问。而且查询条件也是动态的,比如

if (this) result = result.Where(x=> this ...)
if (that) result = result.Where(x=> that ...)
finally result.ToList() 

我想知道,每次我从缓存中访问列表时,系统是否会在开始构建 linq 查询之前创建数据副本?如果是这样,这就像每次击键复制一样,效率不高。还是它推迟了查询,因为我正在检索 AsQueryable 并构建 linq?

还有更好的选择吗?谢谢

【问题讨论】:

【参考方案1】:

不,MemoryCache 不会复制。您基本上将某个对象实例的引用存储在缓存中,这就是您在访问缓存中的项目时得到的。

我没有正式的文档链接,但在实践中发现了“艰难的方式”,我不小心只使用我返回的引用(没有复制它)修改了缓存的对象。

此外,研究参考来源 (http://referencesource.microsoft.com) 表明没有发生自动复制。

根据您的应用程序和需求,您可能希望确保缓存的类型在设计上实际上是不可变的。

【讨论】:

我尝试了以下测试以测试您所说的内容。 list1=_cache.Get(...); list1=list1.Take(2).ToList(); list2=_cache.Get(...); 如果引用同一个对象,列表不应该被截断为 2 吗?但似乎我每次都有完整的清单。 否,因为您正在创建一个新的列表实例。尝试在从缓存中获取的引用上调用 Remove 做了一些测试。看起来任何返回 List 的操作都会创建一个副本并随后更改 list1 引用。不确定每次改进时 IQueryable 发生了什么,但由于它直到最终 ToList 才实现,所以我想我很好。谢谢所有的细节。【参考方案2】:

无需迷失在 MemoryCache 的细节中,您可以使用基本的 .NET 设计原则来推断这一点。只有值类型易于复制。除了 [Serializable] 和非常损坏的 ICloneable 之外,没有复制引用类型的通用机制。这不是将对象放入 MemoryCache 的要求。所以没有。

缓存对象非常非常简单。一个简单的 List 就可以完成这项工作。您从 MemoryCache 获得的增值是有效缓存的其他基本特征。退休政策。没有策略的缓存是内存泄漏。

【讨论】:

你的意思是缓存、获取和查询都引用同一个数据副本?这似乎是有道理的。还有我在缓存服务中的CacheItemPolicy,就是你说的退休策略吧?

以上是关于访问 MemoryCache 会创建副本吗?的主要内容,如果未能解决你的问题,请参考以下文章

为啥分配为 None 时会创建副本?

enumerate 是不是会创建其参数的副本?

使用索引/范围对数组进行切片是不是会创建数组的副本

将数据从 hdfs 导入到 hbase 是不是会创建一个副本

每次在其中输入内容时,JTextField 都会创建自身的副本[关闭]

std::vector 的 push_back 是不是会创建参数的深层副本?