Есть ли простое, эффективное Map
реализация, которая позволяет пределу на память использоваться картой.
Мой вариант использования - то, что я хочу выделить динамично большую часть памяти, доступной во время ее создания, но я не хочу OutOFMemoryError
в любое время в будущем. В основном я хочу использовать эту карту в качестве кэша, но но я хочу избежать тяжелых реализаций кэша как EHCache
. Моя потребность проста (самое большее алгоритм LRU)
Я должен далее разъяснить, что объекты в моем кэше char[]
или подобные примитивы, которые не будут содержать ссылки на другие объекты.
Я могу поместить верхний предел макс. размера для каждой записи.
спасибо за ответы, ребята!
Как указал jasonmp85, LinkedHashMap имеет конструктор, который позволяет порядок доступа. Я упустил этот момент, когда просматривал документацию API. Реализация также выглядит довольно эффективной (см. ниже). В сочетании с ограничением максимального размера для каждой записи это должно решить мою проблему.
Я также присмотрюсь к SoftReference. К сведению, Google Collections, похоже, имеет довольно хороший 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);
}
Для кэшей гораздо больше подходит SoftHashMap
, чем WeakHashMap
. WeakhashMap обычно используется, когда вы хотите сохранить ассоциацию с объектом до тех пор, пока этот объект жив, но без предотвращения его восстановления.
В отличие от него, SoftReference
более тесно связан с распределением памяти. Подробнее о различиях см. в Нет SoftHashMap?.
WeakHashMap
также обычно не подходит, так как в нем ассоциация обведена вокруг неправильного пути для кэша - он использует слабые ключи и жесткие значения. То есть, ключ и значение удаляются из карты, когда ключ очищается сборщиком мусора. Это, как правило, не то, что нужно для кэша - где ключи обычно являются легкими идентификаторами (например, строками или другими простыми типами значений) - кэши обычно работают так, что ключ/значение восстанавливаются при очистке ссылки value.
В коллекциях Commons есть ReferenceMap
где вы можете указать, какие типы ссылок вы хотите использовать для ключей и значений. Для кэша, чувствительного к памяти, вы, вероятно, будете использовать жесткие ссылки для ключей и мягкие ссылки для значений.
Чтобы получить семантику LRU для заданного числа ссылок N, поддерживайте список последних N записей, извлеченных из кэша - по мере извлечения записи из кэша она добавляется в начало списка (и удаляется из хвоста списка). Чтобы это не занимало слишком много памяти, вы можете создать мягкую ссылку и использовать ее как триггер для удаления определенного процента записей из конца списка. (И создать новую мягкую ссылку для следующего триггера)
. Вы можете использовать LinkedHashMap
, чтобы ограничить количество записей в Карта
:
removeEldestEntry (Map.Entry
: возвращаетeldest) истину
, если эта карта должна удалить свою самую старую запись. Этот метод вызывается командамиput
иputAll
после вставки новой записи в карту. Он предоставляет разработчику возможность удалять самую старую запись каждый раз, когда добавляется новая. Это полезно, если карта представляет кеш: это позволяет карте уменьшить потребление памяти за счет удаления устаревших записей.Пример использования: это переопределение позволит карте увеличиваться до 100 записей, а затем удалять самую старую запись каждый раз, когда добавляется новая запись, поддерживая устойчивое состояние 100 записей.
закрытый статический final int MAX_ENTRIES = 100; защищенное логическое значение removeEldestEntry (Map.Вход старший) { размер возврата ()> MAX_ENTRIES; }
Если все, что вам нужно, это карта, ключи которой можно очистить, чтобы избежать OutOfMemoryErrors
, вы можете изучить WeakHashMap . Он использует WeakReferences , чтобы позволить сборщику мусора получить записи карты. Однако он не будет применять какую-либо семантику LRU, кроме тех, которые присутствуют в сборке мусора поколений.
Также существует LinkedHashMap , в документации к которой есть следующее:
Для создать связанную хеш-карту, порядок которой итераций - это порядок, в котором последний доступ к записям был осуществлен с наименее недавно обращались к самый последний (порядок доступа). Этот вид карты хорошо подходит для построения Кеши LRU. Вызов команды "поставить или получить" метод приводит к доступу к соответствующая запись (при условии, что существует после вызова завершает). Метод putAll генерирует один доступ на вход для каждого отображение в указанной карте, в порядок сопоставлений "ключ-значение" обеспечивается записью указанной карты установить итератор. Никаких других методов генерировать доступ к входу. В в частности, операции на Коллекционные представления не влияют на порядок итерации вспомогательной карты.
Таким образом, если вы используете этот конструктор для создания карты, у которой Iterator
итерация выполняется в LRU, обрезать карту становится довольно легко. Единственное (довольно большое ) предостережение заключается в том, что LinkedHashMap вообще не синхронизируется , так что вы сами по себе для параллелизма. Вы можете просто обернуть его синхронизированной оболочкой, но это может вызвать проблемы с пропускной способностью.
Если бы мне пришлось написать свою собственную структуру данных для этого варианта использования, я бы, вероятно, создал какую-то структуру данных с картой, очередью и ReadWriteLock
вместе с потоком уборщика для обработки очистки, когда на карте было слишком много записей. Можно было бы немного превысить желаемый максимальный размер, но в установившемся режиме вы останетесь ниже него.
WeakHashMap
не обязательно достигнет вашей цели, поскольку, если ваше приложение удерживает достаточно сильную ссылку на ключи, вы УВИДИТЕ OOME.
В качестве альтернативы вы можете заглянуть в SoftReference
, которая обнулит содержимое, когда куча станет недостаточной. Однако большинство комментариев, которые я видел, указывают на то, что он не будет обнулять ссылку до тех пор, пока куча не станет действительно очень низкой и многие GC не начнут работать с серьезным снижением производительности (поэтому я не рекомендую использовать его для ваших целей) .
Я рекомендую использовать простую карту LRU, например http://commons.apache.org/collections/apidocs/org/apache/commons/collections/LRUMap.html