MemoryCache的内存限制具体是什么意思?

7

System.Runtime.Caching.MemoryCache是.NET Framework(版本4+)中的一个类,它使用字符串作为键,在内存中缓存对象。与System.Collections.Generic.Dictionary<string, object>不同,该类具有各种各样的功能,让您可以配置缓存可以增长多大(无论是绝对还是相对),为不同的缓存项设置不同的过期策略等等。

我的问题与内存限制有关。 MSDN上的文档似乎都没有令人满意的解释,而Reference Source上的代码也相当晦涩。抱歉将所有这些内容堆叠在一个SO“问题”中,但我无法想出如何将一些问题分离成自己的问题,因为它们实际上只是一个总体问题的不同视图:“如何将惯用的C#/.NET与通用的可配置内存限制的有用内存缓存相协调?”

  1. MemoryCache占用的空间是否包括密钥大小? 对于内部池中的密钥,每个密钥只应将对象引用的大小添加到缓存的大小中,是这样吗?
  2. 当确定在池中存储的对象的大小时,MemoryCache是否考虑存储的对象引用之外的更多因素? 我的意思是...它必须这样做,对于常见情况,否则配置选项会极其误导......对于其余的问题,我将假设它确实这样做。
  3. 鉴于MemoryCache几乎肯定会考虑缓存中存储的值的大小超出对象引用的大小,它有多深?
    1. 如果我要实现类似于此的东西,我会发现很难考虑单个对象的“子”成员的内存使用情况,而不会同时引入“父”引用属性。
    2. 例如,在游戏应用程序中有一个类Player。 Player具有一些特定于玩家的状态,封装在公共PlayerStateData PlayerState {get;}属性中,该属性封装了玩家朝向,他们持有的螺栓数量等信息,以及对整个游戏状态的引用public GameStateData GameState {get;},可以从仅知道玩家的方法返回到游戏的(更大的)状态。
    3. MemoryCache是否在考虑缓存的贡献大小时同时考虑PlayerState和GameState?
    4. 也许更像是“直接存储在缓存中的对象及其成员所占用的托管堆上的总大小,以及通过这些对象的成员可达的所有内容”?
    5. 似乎将 GameState 的贡献限制乘以5只因为有5个玩家被缓存是很愚蠢的...但另一方面,可能的实现可能会这样做,并且难以计算 PlayerState 而不计算 GameState
  4. 如果一个对象在MemoryCache中存储多次,每个条目是否单独计入限制?
  5. 与前一个问题相关,如果一个对象直接存储在MemoryCache中,但也间接通过另一个对象的成员存储,则任何一个对内存限制的影响是什么?
  6. 如果一个对象存储在MemoryCache中,但也被某些与MemoryCache完全断开连接的其他活动对象引用,哪些对象会计入内存限制? 如果它是对象数组,其中一些(但不是全部)具有传入的外部引用,情况又如何?
我的研究让我找到了SRef.cs,但在到达这里后放弃了理解,后来导致了这里。猜测所有这些问题的答案都围绕着找到并沉淀最终填充INT64的代码。

在我看来,MemoryCache限制处理最有用的实现方式将涉及缓存能够完美地回答以下问题:“如果我被清除并且立即运行完整GC,那么在该GC运行期间会释放多少字节的内存?”但是,我越想越觉得,如果缓存的所有者不遵守某些未公开(或至少无法发现)的规则,那么其答案将受到严重的膨胀,因此这与理论原因有关。 - Joe Amenta
我来这里问的正是同样的问题,特别是关于共享状态引用的问题。另一个问题也困扰着我...假设放入缓存的缓存对象(图形)在放入后增长或缩小了怎么办?这一切都相当模糊。 - spender
1个回答

1
我知道现在有点晚了,但是我已经在源代码中进行了大量挖掘,试图理解发生了什么,现在我有一个相当好的想法。我要说的是,MemoryCache是MSDN上文档最差的类之一,这让我感到困惑,因为它旨在供那些试图优化他们的应用程序的人使用。
MemoryCache使用特殊的“大小引用”来测量对象的大小。在内存缓存源代码中,这看起来像是一个巨大的黑客,涉及反射来包装一个名为“System.SizedReference”的内部类型。据我所知,这会导致GC在第2代收集期间设置它所指向的对象图的大小。
经过我的测试,这将包括父对象的大小,因此也包括由父对象引用的所有子对象等,但是我发现,如果将对父对象的引用弱化(即通过WeakReferenceWeakReference<>),则不再计算为对象图的一部分,因此这就是我现在为所有缓存对象所做的事情。
我认为缓存对象需要完全自包含或使用对其他对象的弱引用才能使内存限制起作用。
如果您想自己尝试,请复制SRef.cs中的代码,创建一个对象图并将新的SRef实例指向它,然后调用GC.Collect。收集后,近似大小将设置为对象图的大小。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接