通过Javascript WeakMaps进行垃圾回收缓存

通过Javascript WeakMaps进行垃圾回收缓存,javascript,caching,garbage-collection,ecmascript-6,ecmascript-harmony,Javascript,Caching,Garbage Collection,Ecmascript 6,Ecmascript Harmony,我想在JS中缓存大对象。这些对象是通过键检索的,缓存它们是有意义的。但是它们不能一次装入内存,所以我希望在需要时对它们进行垃圾收集——GC显然知道得更好 使用其他语言中的WeakReference或WeakValueDictionary创建这样的缓存是非常简单的,但是在ES6中,我们使用了WeakMap,其中键很弱 那么,是否可以创建类似于WeakReference的东西,或者从WeakMap创建垃圾收集缓存 是否可以从WeakMap生成WeakReference或从WeakMap生成垃圾回收缓

我想在JS中缓存大对象。这些对象是通过键检索的,缓存它们是有意义的。但是它们不能一次装入内存,所以我希望在需要时对它们进行垃圾收集——GC显然知道得更好

使用其他语言中的WeakReference或WeakValueDictionary创建这样的缓存是非常简单的,但是在ES6中,我们使用了WeakMap,其中键很弱

那么,是否可以创建类似于
WeakReference
的东西,或者从
WeakMap
创建垃圾收集缓存

是否可以从WeakMap生成WeakReference或从WeakMap生成垃圾回收缓存


答:两个问题的答案都是“否”。

有两种情况下,哈希映射较弱是有用的(您的哈希映射似乎适合第二种情况):

  • 希望将信息附加到具有已知身份的对象上;如果该对象不再存在,所附信息将变得毫无意义,同样也应不再存在。JavaScript支持这种场景

  • 人们希望合并对语义相同对象的引用,以减少存储需求并加快比较。例如,使用对同一子树的引用替换对相同大型子树的许多引用可以在内存使用和执行时间上实现数量级的减少。不幸的是,JavaScript不支持这种情况

  • 在这两种情况下,只要表中的引用是有用的,它们就会保持活动状态,当它们变得无用时,“自然”就有资格被收集。不幸的是,
    WeakReference
    的设计者并没有为上面定义的两种用法实现单独的类,而是将其设计成可以在某种程度上对其中任何一种都可用,尽管不是很好


    在键定义相等表示引用标识的情况下,
    WeakHashMap
    将满足第一种使用模式,但第二种使用模式将毫无意义(持有对语义上与存储密钥相同的对象的引用的代码将持有对存储密钥的引用,并且不需要WeakHashMap为其提供引用)。在键定义其他形式的相等的情况下,表查询返回对存储对象的引用以外的任何内容通常都没有意义,但避免存储引用使键保持活动状态的唯一方法是使用
    WeakHashMap
    并让客户端检索弱引用,检索键存储在其中的引用,并检查它是否仍然有效(在
    WeakHashMap
    返回
    WeakReference
    和检查
    WeakReference
    本身之间可以收集它).

    正如其他答案所提到的,不幸的是,没有像Java/C中那样的弱映射

    作为一种解决方法,我创建了这个
    CacheMap
    ,它可以保留最大数量的对象,并跟踪它们在设定时间段内的使用情况,以便您:

  • 必要时,始终删除访问最少的对象
  • 不要造成内存泄漏
  • 这是密码

    "use strict";
    
    /**
     * This class keeps a maximum number of items, along with a count of items requested over the past X seconds.
     * 
     * Unfortunately, in JavaScript, there's no way to create a weak map like in Java/C#.  
     * See https://stackoverflow.com/questions/25567578/garbage-collected-cache-via-javascript-weakmaps
     */
    module.exports = class CacheMap {
      constructor(maxItems, secondsToKeepACountFor) {
        if (maxItems < 1) {
          throw new Error("Max items must be a positive integer");
        }
        if (secondsToKeepACountFor < 1) {
          throw new Error("Seconds to keep a count for must be a positive integer");
        }
    
        this.itemsToCounts = new WeakMap();
        this.internalMap = new Map();
        this.maxItems = maxItems;
        this.secondsToKeepACountFor = secondsToKeepACountFor;
      }
    
      get(key) {
        const value = this.internalMap.get(key);
        if (value) {
          this.itemsToCounts.get(value).push(CacheMap.getCurrentTimeInSeconds());
        }
        return value;
      }
    
      has(key) {
        return this.internalMap.has(key);
      }
    
      static getCurrentTimeInSeconds() {
        return Math.floor(Date.now() / 1000);
      }
    
      set(key, value) {
        if (this.internalMap.has(key)) {
          this.internalMap.set(key, value);
        } else {
          if (this.internalMap.size === this.maxItems) {
            // Figure out who to kick out.
            let keys = this.internalMap.keys();
            let lowestKey;
            let lowestNum = null;
            let currentTime = CacheMap.getCurrentTimeInSeconds();
            for (let key of keys) {
              const value = this.internalMap.get(key);
              let totalCounts = this.itemsToCounts.get(value);
              let countsSince = totalCounts.filter(count => count > (currentTime - this.secondsToKeepACountFor));
              this.itemsToCounts.set(value, totalCounts);
              if (lowestNum === null || countsSince.length < lowestNum) {
                lowestNum = countsSince.length;
                lowestKey = key;
              }
            }
    
            this.internalMap.delete(lowestKey);
          }
          this.internalMap.set(key, value);
        }
        this.itemsToCounts.set(value, []);
      }
    
      size() {
        return this.internalMap.size;
      }
    };
    

    我认为这个问题是专门针对JavaScript的,而不是Java或其他具有
    WeakReference
    /
    WeakHashMap
    的语言。问题是JavaScript只有类似于
    WeakHashMap
    的东西,但不等同于
    WeakReference
    @Qantas94Heavy:确实如此。也许我应该这样做我认为JavaScript只支持第一个,不幸的是,这不是我怀疑OP真正想要的。你喜欢编辑吗?在JC中就是。因此,使用WeakRefs进行缓存没有任何意义。如果需要缓存大型对象,请使用浏览器的缓存(如果它们来自服务器)或。在节点环境中使用Redis或Memcached。
    // Keeps at most 10 client databases in memory and keeps track of their usage over a 10 min period.
    let dbCache = new CacheMap(10, 600);