Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

Как указывал jasonmp85, LinkedHashMap имеет конструктор, который разрешает доступ. Я пропустил этот бит, когда посмотрел документы API. Реализация также выглядит достаточно эффективной (см. Ниже). В сочетании с максимальным размером крышки для каждой записи, это должно решить мою проблему.

Я также буду внимательно смотреть на SoftReference. Как раз для записи, Google Collections, похоже, имеет довольно хороший API для SoftKeys и SoftValues ​​и Maps в целом.

Вот фрагмент из класса 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);
        }

Ответ 2

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

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

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

private static final int MAX_ENTRIES = 100;

protected boolean removeEldestEntry(Map.Entry eldest) {
    return size() > MAX_ENTRIES;
}

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

Там также LinkedHashMap, что есть в документации:

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

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

Сбросьте свое собственное решение

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

Ответ 5

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

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

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