0

假设我有一个类似下面的片段,它返回 Context.Cache["someComplexObject"] 的内容:

public class Something {
    private static SortedDictionary<Guid, Object> ComplexObjectCache
    {
        get
        {
            if (Context.Cache["someComplexObject"] == null)
            {
                FillCache();
            }
            return (SortedDictionary<Guid, Object>)Context.Cache["someComplexObject"];
        }

        set
        {
            Int32 seconds = (new Random()).Next(120, 180);
            Context.Cache.Add(
                "someComplexObject",
                value,
                null,
                DateTime.Now.AddSeconds(seconds),
                System.Web.Caching.Cache.NoSlidingExpiration,
                System.Web.Caching.CacheItemPriority.NotRemovable,
                null
            );
        }
    }
}

在用户控件中,假设我有这个:

Object o = Something.ComplexObjectCache[someGuid];

/* 

    do some other stuff ...

*/

DoSomethingWith(o);

在非常罕见的情况下,我会看到一些异常,这些异常让我相信当DoSomethingWith(o)被击中时o不再在内存中。我 97% 确定没有其他并行进程发生,除了缓存在做什么。我看到其他奇怪的现象让我相信缓存键是空的或不完整的。

即使在其他地方仍有指向它的指针,缓存能否/是否真的从内存中删除一个对象?如果是这样,我该如何对抗?

更新

经过进一步挖掘,我发现我们有一个相关的缓存项(索引),当它在“主”缓存项之前过期时会导致问题。我已经重做了这个实体的缓存代码,将这两个结构一起存储在一个对象数组中。这似乎解决了这个问题。

你们都没有足够的信息来解决问题。因此,我将查看答案,并对似乎最准确地解决了给定问题的答案打勾。

4

4 回答 4

1

缓存永远不会从内存中删除任何内容。

它是从内存中删除对象的垃圾收集器,并且只有在没有更多对该对象的引用时才会发生这种情况。因此,对象不会从内存中删除,即使它们从缓存中删除,只要您有对它的引用。

(缓存可以在请求中丢弃对对象的引用。由于多个线程可以同时处理请求,因此在不同的线程中可能会发生一些事情,从而使缓存丢弃项目。缓存不能等到所有线程都空闲才能丢弃项目,那么它可能永远无法做到这一点,并且您会耗尽内存。)

于 2012-10-19T22:19:20.790 回答
1

是的,它会。如果 Web 服务器被按下内存,即使这样的代码也将无法打印“foo”。

Context.Cache.Add(
            "someComplexObject",
            "foo",
            null,
            DateTime.Now.AddSeconds(seconds),
            System.Web.Caching.Cache.NoSlidingExpiration,
            System.Web.Caching.CacheItemPriority.NotRemovable,
            null
        );

Response.Write(Cache["someComplexObject").ToString());

你需要做一些聪明的事情,比如

string val = "foo";

Context.Cache.Add(
            "someComplexObject",
            val,
            null,
            DateTime.Now.AddSeconds(seconds),
            System.Web.Caching.Cache.NoSlidingExpiration,
            System.Web.Caching.CacheItemPriority.NotRemovable,
            null
        );

Response.Write(val.ToString());

此外,您在读取或写入缓存的任何时候都需要锁定。您可以为此使用 ReaderWriterLock (或类似的)。这是为了避免在写入缓存时读取(以及在读取缓存时写入)。

我已经为SixPack 库实现了几个缓存,您可能会觉得它们很有趣,例如查看这个CacheController类。

附带说明一下,该类比您需要的要复杂一点,因为它是允许您像这样缓存的系统的一部分:

[Cached]
public class MyTime : ContextBoundObject
{
        [CachedMethod(1)]
        public DateTime Get()
        {
                Console.WriteLine("Get invoked.");
                return DateTime.Now;
        }

        public DateTime GetNoCache()
        {
                Console.WriteLine("GetNoCache invoked.");
                return DateTime.Now;
        }
}

完整示例

NuGet 包

于 2012-10-19T22:26:39.993 回答
0

只要感觉如此,缓存就可以自由地删除对象。即,它可以检测内存压力并在添加项目后立即删除它们。

你真的不应该使用 Cache 来传递对象。缓存是缓存 - 存储对象以在将来加快速度,但在单个请求期间使用其他方式传递对象。另请注意,与Items之类的东西不同,Cache 是共享的。

于 2012-10-19T22:17:38.273 回答
0

是的,您的 FillCache 方法正在填充缓存,但是在您检索缓存内容之间存在时间。此时您的缓存可能会失效。尝试从您的 FillCache 方法返回,以便您始终拥有可靠的参考:

private SortedDictionary<Guid, Object> ComplexObjectCache()
{
    if (Context.Cache["someComplexObject"] == null)
    {
       var cacheItems = FillCache();
       return cacheItems;
    }
    return (SortedDictionary<Guid, Object>)Context.Cache["someComplexObject"];
}
于 2012-10-19T22:18:06.907 回答