10

Ich möchte große Objekte in JS Cache. Diese Objekte werden per Schlüssel abgerufen, und es ist sinnvoll, sie zwischenzuspeichern. Aber sie werden nicht alle gleichzeitig in den Speicher passen, also möchte ich, dass sie bei Bedarf Müll gesammelt werden - der GC weiß es offensichtlich besser.Müll gesammelt Cache über Javascript WeakMaps

Es ist ziemlich trivial, eine solche Cache mit WeakReference oder WeakValueDictionary in anderen Sprachen, aber in ES6 haben wir WeakMap statt gefunden zu machen, wo Schlüssel schwach sind.

Also, ist es möglich, etwas wie ein WeakReference zu machen oder müllsammelte Caches von WeakMap zu machen?

Antwort

2

ist es möglich, WeakReference von WeakMap zu machen oder Garbage-Collected Cache von WeakMap zu machen?

AFAIK die Antwort ist "Nein" zu beiden Fragen.

4

Es gibt zwei Szenarien, in denen es sinnvoll ist, für eine Hash-Karte, schwach zu sein (Sie scheint den zweiter zu passen):

  1. Man möge mit einer bekannten Identitätsinformationen zu einem Objekt befestigen; Wenn das Objekt aufhört zu existieren, wird die angehängte Information bedeutungslos und sollte ebenfalls aufhören zu existieren. JavaScript unterstützt dieses Szenario.

  2. Man möchte Referenzen auf semantisch identische Objekte zusammenführen, um Speicheranforderungen zu reduzieren und Vergleiche zu beschleunigen. Das Ersetzen vieler Verweise auf identische große Teilbäume, z. B. mit Verweisen auf den gleichen Teilbaum, kann eine Verringerung der Speicherbelegung und Ausführungszeit um Größenordnungen ermöglichen. Leider unterstützt JavaScript dieses Szenario nicht.

In beiden Fällen Verweise in der Tabelle werden am Leben, so lange aufbewahrt, wie sie nützlich sind, und „natürlich“ für die Sammlung geeignet werden, wenn sie unbrauchbar werden. Leider haben die Designer von WeakReference, statt separate Klassen für die beiden oben definierten Anwendungen zu implementieren, es so gemacht, dass es für beide verwendbar ist, wenn auch nicht besonders gut. In den Fällen, in denen die Schlüssel die Gleichheit für die Referenzidentität definieren, erfüllt WeakHashMap das erste Verwendungsmuster, aber die zweite wäre bedeutungslos (Code, der einen Verweis auf ein Objekt enthält, das semantisch mit einem gespeicherten Schlüssel identisch ist, würde a enthalten.) Referenz auf den gespeicherten Schlüssel, und würde die WeakHashMap nicht benötigen, um es zu geben). In Fällen, in denen Schlüssel eine andere Form der Gleichheit definieren, ist es im Allgemeinen nicht sinnvoll, dass eine Tabellenabfrage etwas anderes als einen Verweis auf das gespeicherte Objekt zurückgibt, aber die einzige Möglichkeit zu vermeiden, dass der gespeicherte Verweis den Schlüssel am Leben erhält, ist Verwenden Sie einen WeakHashMap<TKey,WeakReference<TKey>> und lassen Sie den Client die schwache Referenz abrufen, die darin gespeicherte Schlüsselreferenz abrufen und prüfen, ob sie noch gültig ist (sie könnte zwischen der WeakHashMap die WeakReference und der WeakReference selbst überprüft wird).

+0

Ich denke, diese Frage speziell auf JavaScript bezieht, nicht Java oder einer anderen Sprache, die 'WeakReference' /' WeakHashMap' hat. Das Problem ist, dass JavaScript nur eine "WeakHashMap" hat, aber nicht das Äquivalent einer 'WeakReference'. –

+0

@ Qantas94Heavy: In der Tat so. Vielleicht hätte ich angeben sollen, dass JavaScript nur das erste unterstützt, was leider nicht das ist, was das OP wirklich will. Gefällt dir die Bearbeitung? – supercat

0

Wie die anderen Antworten erwähnt haben, gibt es leider keine schwache Karte, wie es sie in Java/C# gibt.

Als Arbeit um, habe ich diese CacheMap, die sich um eine maximale Anzahl von Objekten hält, und verfolgt deren Nutzung über einen bestimmten Zeitraum, so dass Sie:

  1. immer die am wenigsten zugegriffen Objekt entfernen, wenn erforderlich
  2. Erstellen Sie kein Speicherleck.

Hier ist der Code.

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

Und Sie nennen es etwa so:

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