Принудительное освобождение большого объекта кеша в Java - PullRequest
9 голосов
/ 09 марта 2010

Я использую хэш-карту больших (миллионов) записей для кэширования значений, необходимых для алгоритма, ключ представляет собой комбинацию двух объектов long. Поскольку он постоянно растет (поскольку ключи на карте меняются, а старые больше не нужны), было бы неплохо иметь возможность принудительно стереть все содержащиеся в нем данные и начать заново во время выполнения, есть ли способ сделать это эффективно? в Java?

Я имею в виду освободить связанную память (около 1-1,5 ГБ hashmap) и перезапустить с пустой hashmap ..

Ответы [ 7 ]

13 голосов
/ 09 марта 2010

Звучит так, как будто вам нужна WeakHashMap вместо:

Реализация Map на основе хеш-таблиц со слабыми ключами. Запись в WeakHashMap будет автоматически удалена, если ее ключ больше не используется. Точнее говоря, наличие сопоставления для данного ключа не помешает тому, чтобы ключ был отброшен сборщиком мусора, то есть сделан финализируемым, завершенным и затем восстановленным. Когда ключ отбрасывается, его запись эффективно удаляется с карты, поэтому этот класс ведет себя несколько иначе, чем другие реализации Map.

Я не уверен, как это работает с Long в качестве ключей. Также это может представлять интерес:

WeakHashMap - это не кеш! Понимание WeakReference и SoftReference

13 голосов
/ 09 марта 2010

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

Что касается восстановления памяти, вам придется позволить сборщику мусора выполнять свою работу.

Ваши ценности тоже длинные? В этом случае вы можете захотеть взглянуть на более эффективную память реализации, чем универсальный HashMap, такой как TLongLongHashMap, найденный в библиотеке GNU Trove . Это должно сэкономить много памяти.

3 голосов
/ 09 марта 2010

Для кэша с поддержкой памяти вы можете использовать Коллекции Apache Commons , в частности их класс org.apache.commons.collections.map.ReferenceMap. Специальная операция Java - это мягкая ссылка . Java предоставляет WeakHashMap для слабых ссылок, но слабые ссылки - это не то, что вам нужно для кэша. Java не предоставляет SoftHashMap, но ReferenceMap от Apache Commons может быть подходящей заменой.

Осознание в памяти мягких ссылок несколько грубовато и негибко. Вы можете поиграть с некоторыми опциями Java, чтобы как-то их настроить, особенно значение -XX:SoftRefLRUPolicyMSPerMB, которое выражает (в миллисекундах), как долго значения с мягкой ссылкой хранятся в памяти (когда они перестают быть непосредственно доступными). Например, с этим:

java -XX:SoftRefLRUPolicyMSPerMB=2500

тогда JVM попытается сохранить кэшированное значение на 2,5 секунды больше, чем это было бы с WeakHashMap.

Если мягкие ссылки не предоставляют то, что вы ищете, вам придется реализовать собственную стратегию кэширования и, действительно, сбросить карту вручную. Это ваш начальный вопрос. Для очистки вы можете использовать метод clear() или просто создать новый HashMap. Разница должна быть незначительной, и у вас могут возникнуть проблемы, просто измерив эту разницу.

Чередование между «полным кешем» и «пустым кешем» также может считаться немного грубым, поэтому вы можете поддерживать несколько карт. Например, вы поддерживаете десять карт. Когда вы ищете кэшированное значение, вы просматриваете все карты, но когда у вас есть значение, вы помещаете его только в первую карту. Когда вы хотите сбросить карту, вы поворачиваете карты: первая карта становится второй, вторая становится третьей и так далее, вплоть до десятой карты, которая сбрасывается. Новая свежая первая карта создана. Это будет выглядеть так:

import java.util.*;

public class Cache {

    private static final int MAX_SIZE = 500000;

    private Map[] backend;
    private int size = 0;

    public Cache(int n)
    {
        backend = new Map[n];
        for (int i = 0; i < n; i ++)
            backend[i] = new HashMap();
    }

    public int size()
    {
        return size;
    }

    public Object get(Object key)
    {
        for (Map m : backend) {
            if (m.containsKey(key))
                return m.get(key);
        }
        return null;
    }

    public Object put(Object key, Object value)
    {
        if (backend[0].containsKey(key))
            return backend[0].put(key, value);
        int n = backend.length;
        for (int i = 1; i < n; i ++) {
            Map m = backend[i];
            if (m.containsKey(key)) {
                Object old = m.remove(key);
                backend[0].put(key, value);
                return old;
            }
        }
        backend[0].put(key, value);
        size ++;
        while (size > MAX_SIZE) {
            size -= backend[n - 1].size();
            System.arraycopy(backend, 0, backend, 1, n - 1);
            backend[0] = new HashMap();
        }
        return null;
    }
}

Приведенный выше код полностью не проверен и должен быть дополнен обобщениями. Тем не менее, он иллюстрирует основные идеи: все карты тестируются при чтении (get()), все новые значения переходят на первую карту, общий размер сохраняется, а когда размер превышает заданный предел, карты поворачиваются. Обратите внимание, что существует специальная обработка, когда новое значение устанавливается для известного ключа. Кроме того, в этой версии ничего особенного не делается при поиске кэшированного значения, но мы можем «омолодить» доступное кэшированное значение: при get(), когда значение найдено, но не на первой карте, оно может быть перемещено в первую карта. Таким образом, часто используемые значения остаются в кэше навсегда.

3 голосов
/ 09 марта 2010

Очистить хэш-карту:

hashmap.clear();

Затем запустите сборщик мусора:

Runtime.getRuntime().gc();

Это страница Javadoc для Runtime.gc () .

0 голосов
/ 02 мая 2015

Вместо использования HashMap или другой реализации карты в качестве кэша, вы можете попробовать использовать среду, специализирующуюся на кэшировании. Хорошо известная среда кэширования для Java: Ehcache .

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

0 голосов
/ 09 марта 2010

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

Только мои 2 цента:)

0 голосов
/ 09 марта 2010

Вы смотрели на WeakHashMap ?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...