python - 将 WeakValueDictionary 用于缓存时的 GC 问题

标签 python caching garbage-collection weak-references

根据 weakref 模块的官方 Python 文档,“弱引用的主要用途是实现包含大对象的缓存或映射,...”。因此,我使用 Wea​​kValueDictionary 为长时间运行的函数实现缓存机制。然而,事实证明,缓存中的值在实际再次使用之前永远不会保留在那里,而是几乎每次都需要重新计算。由于对存储在 WeakValueDictionary 中的值的访问之间没有强引用,因此 GC 摆脱了它们(即使内存绝对没有问题)。

现在,我应该如何使用弱引用来实现缓存?如果我在某处明确保留强引用以防止 GC 删除我的弱引用,那么一开始就没有必要使用 Wea​​kValueDictionary。 GC 可能应该有一些选项告诉它:仅当内存用完(或超过某个阈值)时,删除所有没有引用的所有内容和所有具有弱引用的内容。有类似的东西吗?或者对于这种缓存有更好的策略吗?

最佳答案

我将尝试通过如何使用 weakref 模块实现缓存的示例来回答您的询问。我们会将缓存的弱引用保存在 weakref.WeakValueDictionary 中,将强引用保存在 collections.deque 中,因为它具有 maxlen 属性控制它持有多少对象。以函数闭包方式实现:

import weakref, collections
def createLRUCache(factory, maxlen=64):
    weak = weakref.WeakValueDictionary()
    strong = collections.deque(maxlen=maxlen)

    notFound = object()
    def fetch(key):
        value = weak.get(key, notFound)
        if value is notFound:
            weak[key] = value = factory(key)
        strong.append(value)
        return value
    return fetch

deque 对象将只保留最后的 maxlen 条目,一旦达到容量就简单地删除对旧条目的引用。当旧条目被丢弃并被 python 收集为垃圾时,WeakValueDictionary 将从映射中删除这些键。因此,这两个对象的组合帮助我们在 LRU 缓存中仅保留 maxlen 条目。

class Silly(object):
    def __init__(self, v):
        self.v = v

def fib(i):
    if i > 1:
        return Silly(_fibCache(i-1).v + _fibCache(i-2).v)
    elif i: return Silly(1)
    else: return Silly(0)
_fibCache = createLRUCache(fib)

关于python - 将 WeakValueDictionary 用于缓存时的 GC 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9687921/

相关文章:

python - 如何从 Ableton Live 控制表面脚本将 MIDI 数据发送到我的 Arduino 板

c# - Redis 弹出列表项 按项目数

iphone - 在 iPhone 中缓存来自远程服务器的图像

javascript - 自调用函数+闭包改善垃圾收集导致的挂起

java - 如何将GC日志写入命名管道

python - 为什么 cProfile 会导致函数返回不同的值?

python - 在 Selenium Unittest 中同时断言两个语句为 true

spring - Redis 还是 Ehcache?

java - WeakReferenced 对象在调用 System.gc() 后未被垃圾回收

python - 可插入的Python程序