Карта фиксированной памяти Java
есть ли простой, эффективный Map
реализация, которая позволяет ограничить объем памяти, используемой картой.
мой вариант использования заключается в том, что я хочу, чтобы динамически выделять больше памяти, доступной на момент его создания, но я не хочу!--1--> в любое время в будущем. В принципе, я хочу использовать эту карту в качестве кэша, но я хочу избежать тяжелых реализаций кэша, таких как EHCache
. Моя потребность проста (максимум алгоритм LRU)
Я должен уточнить, что объекты в моем кэше char[]
или аналогичные примитивы, которые не будут содержать ссылок на другие объекты.
Я могу поставить верхний предел максимального размера для каждой записи.
5 ответов
можно использовать LinkedHashMap
ограничить количество записей в Map
:
removeEldestEntry(Map.Entry<K,V> eldest)
: Returnstrue
если эта карта должна удалить свою старшую запись. Этот метод вызывается с помощьюput
иputAll
после вставки новой записи в карте. Он предоставляет исполнителю возможность удалять самую старую запись при каждом добавлении новой. Это полезно, если карта представляет кэш: это позволяет карте уменьшить объем памяти потребление путем удаления устаревших записей.пример использования: это переопределение позволит карте вырасти до 100 записей, а затем удалить самую старую запись каждый раз, когда добавляется новая запись, поддерживая устойчивое состояние 100 записей.
private static final int MAX_ENTRIES = 100; protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; }
вопросы
для тайников, a SoftHashMap
гораздо более уместно, чем WeakHashMap
. WeakhashMap обычно используется, когда вы хотите поддерживать связь с объектом до тех пор, пока этот объект жив, но не препятствуя его восстановлению.
напротив, a SoftReference
более тесно связан с выделением памяти. См.Нет SoftHashMap? для получения подробной информации о различиях.
WeakHashMap
также обычно не подходит, поскольку он имеет ассоциацию вокруг неправильный способ для кэша-он использует слабые ключи и жесткие значения. То есть, ключ и значение удаляются с карты, когда ключ очищается сборщиком мусора. Обычно это не то, что вы хотите для кэша, где ключи обычно являются облегченными идентификаторами (например, строки или какой - либо другой простой тип значения) - кэши обычно работают так, что ключ/значение восстанавливается, когда стоимостью ссылка очищается.
коллекции Commons имеют ReferenceMap
где можно подключить какие типы ссылок, которые вы хотите использовать для ключей и значений. Для кэша, чувствительного к памяти, вы, вероятно, будете использовать жесткие ссылки для ключей и мягкие ссылки для значений.
чтобы получить семантику LRU для заданного количества ссылок N, ведите список последних N записей, извлеченных из кэша - при извлечении записи из кэша она добавляется в начало списка (а хвост списка удаляется.) Обеспечить, чтобы это не удерживая слишком много памяти, вы можете создать мягкую ссылку и использовать ее в качестве триггера для вытеснения процента записей из конца списка. (И создайте новую мягкую ссылку для следующего триггера.)
Решения Платформы Java
если все, что вы ищете, это карта, ключи которой можно очистить, чтобы избежать OutOfMemoryErrors
, вы можете посмотреть в WeakHashMap. Он использует WeakReferences для того, чтобы позволить сборщику мусора пожинать записи карты. Однако он не будет применять никакой семантики LRU, за исключением тех, которые присутствуют в сборке мусора поколений.
там же LinkedHashMap, который в документация:
специальный конструктор предоставляется создание связанной хэш-карты, порядок которой итерации-это порядок, в котором последний доступ к записям, из наименее недавно использовавшихся для совсем недавно (access-order). Этот вид карты хорошо подходит для строительства Кэшей LRU. Вызов put или get метод приводит к доступу к соответствующая запись (при условии, что существует после вызова завершает.) Метод putAll создает одну запись доступ для каждого отображение в указанной карте, в порядок отображения значений ключей вхождение указанной карты установить итератор. Никакой другой метод создание доступа к записи. В частности, операции по коллекция-представления не влияют на порядок итерации резервной карты.
Итак, если вы используете этот конструктор для создания карты,Iterator
повторяется в LRU, становится довольно легко обрезать карту. Один (довольно большой) предостережение что LinkedHashMap составляет не синхронизируется, так что вы сами по себе для параллелизма. Вы можете просто обернуть его в синхронизированную оболочку, но это может иметь проблемы с пропускной способностью.
Сверните Свое Собственное Решение
если бы мне пришлось написать свою собственную структуру данных для этого случая использования, я бы, вероятно, создал какую-то структуру данных с картой, очередью и ReadWriteLock
вместе с потоком дворника для обработки очистки, когда слишком много записей было на карте. Было бы можно немного превысить желаемый максимальный размер, но в установившемся состоянии вы останетесь под ним.
WeakHashMap
не обязательно достигнет вашей цели, так как если достаточно сильная ссылка на ключи удерживаются вашим приложением. ты увидишь ума.
в качестве альтернативы вы можете заглянуть в SoftReference
, который будет обнулять содержимое, как только куча будет дефицитной. Тем не менее, большинство комментариев, которые я видел, указывают на то, что он не будет обнулять ссылку, пока куча действительно не будет очень низкой, и много GC начнет пинать с серьезным ударом по производительности (поэтому я не рекомендую использовать его для вашей цели).
моя рекомендация-использовать простую карту LRU, например http://commons.apache.org/collections/apidocs/org/apache/commons/collections/LRUMap.html
спасибо за ответы ребята!
как указал jasonmp85 LinkedHashMap имеет конструктор, который позволяет порядок доступа. Я пропустил этот бит, когда посмотрел на документы API. Реализация также выглядит довольно эффективной (см. ниже). В сочетании с максимальной крышкой размера для каждой записи это должно решить мою проблему.
Я также внимательно посмотрю на SoftReference. Просто для записи, коллекции Google, похоже, имеют довольно хороший API для SoftKeys и SoftValues и карт в генеральный.
вот фрагмент из класса Java LikedHashMap, который показывает, как они поддерживают поведение LRU.
/**
* Removes this entry from the linked list.
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
* Inserts this entry before the specified existing entry in the list.
*/
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
/**
* This method is invoked by the superclass whenever the value
* of a pre-existing entry is read by Map.get or modified by Map.set.
* If the enclosing Map is access-ordered, it moves the entry
* to the end of the list; otherwise, it does nothing.
*/
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}