Карта постоянной памяти Java

Есть ли простое, эффективное Map реализация, которая позволяет пределу на память использоваться картой.

Мой вариант использования - то, что я хочу выделить динамично большую часть памяти, доступной во время ее создания, но я не хочу OutOFMemoryError в любое время в будущем. В основном я хочу использовать эту карту в качестве кэша, но но я хочу избежать тяжелых реализаций кэша как EHCache. Моя потребность проста (самое большее алгоритм LRU)

Я должен далее разъяснить, что объекты в моем кэше char[] или подобные примитивы, которые не будут содержать ссылки на другие объекты.

Я могу поместить верхний предел макс. размера для каждой записи.

13
задан jiaweizhang 24 November 2015 в 16:16
поделиться

5 ответов

спасибо за ответы, ребята!

Как указал 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);
        }
0
ответ дан 1 December 2019 в 22:56
поделиться

Для кэшей гораздо больше подходит SoftHashMap, чем WeakHashMap. WeakhashMap обычно используется, когда вы хотите сохранить ассоциацию с объектом до тех пор, пока этот объект жив, но без предотвращения его восстановления.

В отличие от него, SoftReference более тесно связан с распределением памяти. Подробнее о различиях см. в Нет SoftHashMap?.

WeakHashMap также обычно не подходит, так как в нем ассоциация обведена вокруг неправильного пути для кэша - он использует слабые ключи и жесткие значения. То есть, ключ и значение удаляются из карты, когда ключ очищается сборщиком мусора. Это, как правило, не то, что нужно для кэша - где ключи обычно являются легкими идентификаторами (например, строками или другими простыми типами значений) - кэши обычно работают так, что ключ/значение восстанавливаются при очистке ссылки value.

В коллекциях Commons есть ReferenceMap где вы можете указать, какие типы ссылок вы хотите использовать для ключей и значений. Для кэша, чувствительного к памяти, вы, вероятно, будете использовать жесткие ссылки для ключей и мягкие ссылки для значений.

Чтобы получить семантику LRU для заданного числа ссылок N, поддерживайте список последних N записей, извлеченных из кэша - по мере извлечения записи из кэша она добавляется в начало списка (и удаляется из хвоста списка). Чтобы это не занимало слишком много памяти, вы можете создать мягкую ссылку и использовать ее как триггер для удаления определенного процента записей из конца списка. (И создать новую мягкую ссылку для следующего триггера)

.
6
ответ дан 1 December 2019 в 22:56
поделиться

Вы можете использовать LinkedHashMap , чтобы ограничить количество записей в Карта :

removeEldestEntry (Map.Entry eldest) : возвращает истину , если эта карта должна удалить свою самую старую запись. Этот метод вызывается командами put и putAll после вставки новой записи в карту. Он предоставляет разработчику возможность удалять самую старую запись каждый раз, когда добавляется новая. Это полезно, если карта представляет кеш: это позволяет карте уменьшить потребление памяти за счет удаления устаревших записей.

Пример использования: это переопределение позволит карте увеличиваться до 100 записей, а затем удалять самую старую запись каждый раз, когда добавляется новая запись, поддерживая устойчивое состояние 100 записей.

 закрытый статический final int MAX_ENTRIES = 100;
защищенное логическое значение removeEldestEntry (Map.Вход старший) {
размер возврата ()> MAX_ENTRIES;
}

Связанные вопросы

10
ответ дан 1 December 2019 в 22:56
поделиться

Решения для платформы Java

Если все, что вам нужно, это карта, ключи которой можно очистить, чтобы избежать OutOfMemoryErrors , вы можете изучить WeakHashMap . Он использует WeakReferences , чтобы позволить сборщику мусора получить записи карты. Однако он не будет применять какую-либо семантику LRU, кроме тех, которые присутствуют в сборке мусора поколений.

Также существует LinkedHashMap , в документации к которой есть следующее:

Для создать связанную хеш-карту, порядок которой итераций - это порядок, в котором последний доступ к записям был осуществлен с наименее недавно обращались к самый последний (порядок доступа). Этот вид карты хорошо подходит для построения Кеши LRU. Вызов команды "поставить или получить" метод приводит к доступу к соответствующая запись (при условии, что существует после вызова завершает). Метод putAll генерирует один доступ на вход для каждого отображение в указанной карте, в порядок сопоставлений "ключ-значение" обеспечивается записью указанной карты установить итератор. Никаких других методов генерировать доступ к входу. В в частности, операции на Коллекционные представления не влияют на порядок итерации вспомогательной карты.

Таким образом, если вы используете этот конструктор для создания карты, у которой Iterator итерация выполняется в LRU, обрезать карту становится довольно легко. Единственное (довольно большое ) предостережение заключается в том, что LinkedHashMap вообще не синхронизируется , так что вы сами по себе для параллелизма. Вы можете просто обернуть его синхронизированной оболочкой, но это может вызвать проблемы с пропускной способностью.

Сделайте свое собственное решение

Если бы мне пришлось написать свою собственную структуру данных для этого варианта использования, я бы, вероятно, создал какую-то структуру данных с картой, очередью и ReadWriteLock вместе с потоком уборщика для обработки очистки, когда на карте было слишком много записей. Можно было бы немного превысить желаемый максимальный размер, но в установившемся режиме вы останетесь ниже него.

3
ответ дан 1 December 2019 в 22:56
поделиться

WeakHashMap не обязательно достигнет вашей цели, поскольку, если ваше приложение удерживает достаточно сильную ссылку на ключи, вы УВИДИТЕ OOME.

В качестве альтернативы вы можете заглянуть в SoftReference , которая обнулит содержимое, когда куча станет недостаточной. Однако большинство комментариев, которые я видел, указывают на то, что он не будет обнулять ссылку до тех пор, пока куча не станет действительно очень низкой и многие GC не начнут работать с серьезным снижением производительности (поэтому я не рекомендую использовать его для ваших целей) .

Я рекомендую использовать простую карту LRU, например http://commons.apache.org/collections/apidocs/org/apache/commons/collections/LRUMap.html

1
ответ дан 1 December 2019 в 22:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: