MemoryCache 的内存限制究竟意味着什么?

What, exactly, do MemoryCache's memory limits mean?

System.Runtime.Caching.MemoryCache is a class in the .NET Framework (version 4+) that caches objects in-memory, using strings as keys. More than System.Collections.Generic.Dictionary<string, object>,这个 class 有各种各样的花里胡哨的东西,让你配置缓存可以增长到多大(绝对或相对),设置不同的过期策略不同的缓存项等等。

我的问题与内存限制有关。 None MSDN 上的文档似乎对此进行了令人满意的解释,而 Reference Source 上的代码相当不透明。很抱歉把所有这些都放在一个 SO "question" 中,但我不知道如何将一些内容带入他们自己的问题中,因为它们实际上只是对一个整体问题的不同看法:"how do you reconcile idiomatic C#/.NET with the notion of a generally useful in-memory cache that has configurable memory limits that's implemented nearly entirely in managed code?"

  1. 密钥大小是否计入 MemoryCache 被认为占用的 space?实习生池中的键怎么样,每个键应该只将对象引用的大小添加到缓存的大小?
  2. 在确定存储在池中的对象大小时,MemoryCache 是否考虑的不仅仅是它存储的对象引用的大小? 我的意思是......它必须,对吧?否则,配置选项对于常见情况具有极大的误导性......对于其余问题,我将假设它确实如此。
  3. 鉴于 MemoryCache 几乎肯定会考虑超过缓存中存储的值的对象引用的大小,它有多深?
    1. 如果我要实现这样的东西,我会发现 非常 很难考虑单个对象的 "child" 成员的内存使用情况,而不是同时引入"parent" 参考属性。
    2. 例如,想象一下游戏应用程序中的 class,PlayerPlayer 有一些封装在 public PlayerStateData PlayerState { get; } 属性 中的特定于玩家的状态,封装了玩家正在看的方向、他们持有的链轮数量等,以及参考到整个游戏的状态 public GameStateData GameState { get; },可用于从只了解玩家的方法返回到游戏的(更大的)状态。
    3. MemoryCache 在考虑对缓存的贡献大小时是否同时考虑 PlayerStateGameState
    4. 也许更像 "what's the total size on the managed heap taken up by the objects directly stored in the cache, and everything that's reachable through members of those objects"?
    5. 仅仅因为缓存了 5 个玩家,就将 GameState 对限制的贡献大小乘以 5 似乎是愚蠢的……但话又说回来,一个可能的实现可能就是这样做的,而且不数GameState.
    6. 就很难算PlayerState
  4. 如果一个对象多次存储在 MemoryCache 中,每个条目是否分别计入限制?
  5. 关于上一个,如果一个对象直接存储在MemoryCache中,也间接通过另一个对象的成员存储,这对内存限制有什么影响?
  6. 如果一个对象存储在 MemoryCache 中,但也被其他一些完全与 MemoryCache 断开连接的活动对象引用,哪些对象会违反内存限制?如果它是一个对象数组,其中一些(但不是全部)具有传入的外部引用怎么办?

我自己的研究使我 SRef.cs, which I gave up on trying to understand after getting here, which later leads here。猜测所有这些问题的答案将围绕查找和思考最终填充存储在该句柄中的 INT64 的代码展开。

我知道这已经晚了,但我在源代码中做了很多挖掘工作,试图了解发生了什么,现在我有了一个相当不错的主意。我会说 MemoryCache 是 MSDN 上记录最差的 class,这让我感到困惑,因为它旨在供试图优化其应用程序的人们使用。

MemoryCache 使用特殊的 "sized reference" 来测量 object 的大小。这一切看起来都像是内存缓存源代码中的一个巨大黑客攻击,涉及反射来包装一个名为 "System.SizedReference" 的内部类型,据我所知,这导致 GC 设置它指向的 object 图的大小在第 2 代 collection 期间。

根据我的测试,这将包括 parent object 的大小,因此 parent 引用的所有 child object 等, 但我发现如果你引用 parent object 的弱引用(即通过 WeakReferenceWeakReference<>),那么它不再被算作object 图,这就是我现在对所有缓存 object 所做的。

我认为缓存 object 需要完全 self-contained 或使用对其他 object 的弱引用才能使内存限制完全起作用。

如果你想自己玩一下,只需复制 SRef.cs 中的代码,创建一个 object 图形并将一个新的 SRef 实例指向它,然后调用 GC.Collect .在 collection 之后,近似大小将设置为 object 图的大小。