10

Я хочу кэшировать большие объекты в JS. Эти объекты извлекаются ключом, и имеет смысл кэшировать их. Но они не будут вписываться в память сразу, поэтому я хочу, чтобы они были собраны в мусор, если это необходимо - GC, очевидно, знает лучше.Сбор собранных мусора через Javascript WeakMaps

Это довольно простой способ сделать такой кеш, используя WeakReference или WeakValueDictionary, найденный на других языках, но в ES6 вместо этого мы имеем WeakMap, где ключи являются слабыми.

Итак, можно ли сделать что-то вроде WeakReference или сделать сборщики мусора с WeakMap?

ответ

2

Возможно ли сделать WeakReference от WeakMap или сделать сборщик мусора из WeakMap?

AFAIK ответ «нет» на оба вопроса.

4

Есть два сценария, где это полезно для хэша-карта, чтобы быть слабой (ваш, кажется, соответствует второму):

  1. Один хочет прикрепить информацию к объекту с известной идентичностью; если объект прекращает свое существование, прилагаемая информация станет бессмысленной и также должна прекратиться. JavaScript поддерживает этот сценарий.

  2. Желательно объединить ссылки на семантически идентичные объекты в целях сокращения требований к хранению и ускорения сопоставлений. Замена многих ссылок на одинаковые большие поддеревья, например, со ссылками на одно и то же поддерево, может позволить уменьшить порядок использования памяти и время выполнения. К сожалению, JavaScript не поддерживает этот сценарий.

В обоих случаях ссылки в таблице будут храниться в живых до тех пор, пока они полезны, и «естественно» получают право на инкассо, когда они становятся бесполезными. К сожалению, вместо того, чтобы реализовывать отдельные классы для двух описанных выше способов, разработчики WeakReference сделали это так, чтобы он мог быть полезен для использования, хотя и не очень хорошо.

В тех случаях, когда ключи определяют равенство в среднем ссылочном идентификаторе, WeakHashMap удовлетворяет первому шаблону использования, но второй будет бессмысленным (код, содержащий ссылку на объект, который был семантически идентичен сохраненному ключу, ссылку на сохраненный ключ, и не понадобится WeakHashMap, чтобы дать ему). В тех случаях, когда ключи определяют некоторую другую форму равенства, обычно не имеет смысла, чтобы запрос таблицы возвращал что-либо, кроме ссылки на сохраненный объект, но единственный способ избежать сохранения сохраненной ссылки сохранить ключ в используйте команду WeakHashMap<TKey,WeakReference<TKey>> и попросите клиента получить слабую ссылку, получить сохраненную в ней ссылку ключа и проверить, действительно ли она действительна (она может быть собрана между моментом, когда WeakHashMap возвращает WeakReference и время, которое проверяется WeakReference).

+0

Я думаю, что этот вопрос конкретно со ссылкой на JavaScript, а не Java или какой-либо другой язык, который имеет 'WeakReference' /' WeakHashMap'. Проблема в том, что JavaScript имеет только что-то вроде «WeakHashMap», но не эквивалент «WeakReference». –

+0

@ Qantas94Heavy: Действительно так. Возможно, я должен был указать, что JavaScript поддерживает только первое, что, к сожалению, не та, которую я подозреваю, что действительно хочет OP. Вам нравится редактирование? – supercat

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); 
Смежные вопросы