10

我想緩存在JS大對象。這些對象通過鍵來檢索,並且緩存它們是有意義的。但是他們不會一下子適應記憶,所以我希望他們在需要的時候被垃圾收集--GC顯然知道更好。垃圾收集緩存WeakMaps

使用WeakReference或WeakValueDictionary找到其他語言的緩存是非常簡單的,但在ES6中我們有WeakMap,很弱。

那麼,是否可以製作類似WeakReference的東西,或者是從WeakMap製作垃圾收集緩存?

回答

2

是否可以從WeakMap製作WeakReference或從WeakMap製作垃圾收集緩存?

據我所知,答案是「沒有」這兩個問題。

4

有兩種情況對於一個哈希表疲弱它是非常有用的(你似乎符合第二):

  1. 人們希望連接信息與已知身份的對象;如果對象不復存在,附加的信息將變得毫無意義,並且應該同樣不復存在。 JavaScript支持這種情況。

  2. 人們希望合併引用語義相同的物體,用於減少存儲需求和加快比較的目的。例如,將相同大型子樹的許多引用替換爲相同子樹的引用,可以在數量級上減少內存使用量和執行時間。不幸的是,JavaScript不支持這種情況。

在這兩種情況下,因爲它們是有用的表引用將保持只要活着,並且將「自然」成爲符合回收時,他們變得毫無用處。不幸的是,而不是實施分開類以上定義的兩種用法,的WeakReference設計者使它因此它可以有點-幾分是任一可用的,雖然不是非常好。

如果這些鍵定義相等意味着參考標識符,WeakHashMap將滿足第一個使用模式,但第二個使用模式將是無意義的(代碼中保存對與存儲的鍵在語義上相同的對象的引用將保存引用存儲的密鑰,並且不需要WeakHashMap來給它一個)。在鍵定義其他形式的相等的情況下,對於表查詢返回除了對存儲對象的引用之外的任何內容通常沒有意義,但是避免使存儲的引用保持鍵存活的唯一方法是使用WeakHashMap<TKey,WeakReference<TKey>>並讓客戶端檢索弱引用,檢索存儲在其中的密鑰參考,並檢查它是否仍然有效(它可以得到收集的時間之間的WeakHashMap返回WeakReferenceWeakReference本身已被檢查的時間)。

+0

我認爲這個問題是特別指的JavaScript,而不是Java或具有'WeakReference' /'WeakHashMap'其他語言。問題是JavaScript只有像WeakHashMap這樣的東西,但不是WeakReference的等價物。 – 2015-05-01 00:10:15

+0

@ Qantas94Heavy:確實如此。也許我應該指出,JavaScript只支持第一個,不幸的是這不是我懷疑OP真正想要的那個。你喜歡編輯嗎? – supercat 2015-05-01 15:20:13

0

至於提到的其他答案,不幸的是有沒有這樣的事,作爲一個軟弱的地圖,像有是Java/C#。

作爲工作的時候,我創造了這個CacheMap,保持周圍物體的最大數量,並在設定的時間段跟蹤它們的使用情況,使您:

  1. 務必取下至少訪問的對象,當必要
  2. 不要創建內存泄漏。

這是代碼。

"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; 
    } 
}; 

而且你怎麼稱呼它,像這樣:

// 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);