2010-03-09 2 views
9

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

Я имею в виду освободить связанную с ним память (о 1-1.5gb из HashMap) и перезапустить из пустой HashMap ..

ответ

13

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

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

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

0

Вы взглянули на WeakHashMap?

+1

WeakHashMap работает с идентификатором объекта. Не уверен, что это будет полезно с длинными ключами. – Thilo

+0

Да, я упомянул об этом сомнении и в своем посте. – polygenelubricants

12

Это звучит, как вам нужно WeakHashMap вместо:

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

Я не уверен, как это работает с Long как ключи. Кроме того, это может быть интересно:

WeakHashMap is not a cache! Understanding WeakReference and SoftReference

+5

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

3

Очистить HashMap:

hashmap.clear(); 

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

Runtime.getRuntime().gc(); 

This is the Javadoc page for Runtime.gc().

+6

Я думаю, что вызов 'gc()' явно не рекомендуется, но я могу ошибаться. – polygenelubricants

+0

@polygenelubricants: Javadoc говорит, что это эквивалентно 'System.gc()' (хотя это наоборот, 'System.gc()' говорит, что он эквивалентен 'Runtime.getRuntime(). Gc()'). Нет явного упоминания о том, что один из них не был рекомендован. Ссылка Javadoc размещена вдоль моего сообщения. – zneak

+2

@zneak: Я думаю, что это плохая практика, чтобы вызвать gc явно. Вы должны оставить это в JVM. – Thilo

0

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

Просто мои 2 цента :)

3

Для памяти, известно, кэш, вы можете использовать Apache Commons collections, в частности, их org.apache.commons.collections.map.ReferenceMap класс. Специальная операция Java - soft reference.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(), когда значение найдено, но не на первой карте, его можно перенести на первую карту. Таким образом, часто используемые значения будут сохраняться навсегда.

+0

«Разница должна быть небольшой, и у вас могут даже возникнуть проблемы, просто измеряя эту разницу». Разница заключается в массиве, который содержит записи карты (с сохраняется на clear()). С миллионами записей массив будет несколько MB (я думаю, что Object [] занимает примерно четыре байта на запись) – Thilo

+2

Размер массива довольно мал, чем общий размер объектов с указателем. Кроме того, если это кеш, который вы регулярно опорожняете, то по определению вы снова заполните его, а большой массив * будет возвращаться в какой-то момент. Использование 'clear()' означает, что вы храните массив, но не перераспределяете его позже, а также избегаете некоторого уровня перефразирования, когда внутренний массив растет. Я не думаю, что в долгосрочной перспективе это приведет к заметной разнице. –

0

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

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

Смежные вопросы