2009-10-29 3 views
5

Я хочу создать LinkedHashMap, который ограничит его размер на основе доступной памяти (т. Е. Когда freeMemory + (maxMemory - allocatedMemory) становится ниже определенного порога). Это будет использоваться как форма кеша, возможно, используя «наименее недавно использованную» в качестве стратегии кэширования.LRU LinkedHashMap, который ограничивает размер на основе доступной памяти

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

Например, LinkedHashMap может продолжать удалять элементы, поскольку он считает, что свободной памяти недостаточно, но свободная память не увеличивается, потому что эти удаленные элементы не собираются немедленно сбрасывать мусор.

Есть ли у кого-нибудь опыт в этом отношении? Является ли мое беспокойство оправданным? Если да, может ли кто-нибудь предложить хороший подход?

Я должен добавить, что я также хочу, чтобы иметь возможность «блокировать» кеш, в основном говоря «хорошо, отныне ничего не удалять из-за проблем с использованием памяти».

+0

Какое приложение? Трудно придумать хороший ответ, не имея представления о том, каково фоное намерение. Является ли ваш HashMap кешем или что? – jprete

+0

jprete, я попытался прояснить это - вы правы, его кеш – sanity

+0

Это типичный пример того, где GC и управление памятью Java показывают свое реальное грязное лицо. Было бы так просто * сделать такое на C++. Используя GC, вы не можете выполнять команды типа delete или free. –

ответ

1

Я знаю, что я предвзятый, но я действительно настоятельно рекомендую наш MapMaker для этого. Используйте функцию softKeys() или softValues ​​(), в зависимости от того, является ли это GC-коллекцией ключа или значения, которое более метко описывает, когда запись может быть очищена.

+1

Я бы хотел использовать MapMaker (я широко использую Коллекции Google в моем коде), но есть ли способ заставить его вести себя как кеш LRU, который масштабируется в соответствии с доступной памятью? Кроме того, есть ли способ «заблокировать» его, чтобы он больше не удалял записи в ответ на сбор мусора? – sanity

+1

«ведут себя как кеш LRU ...» - это в значительной степени описывает то, что мягкие ссылки вам дадут, да. «lock» it - единственный разумный ответ на это предполагает копирование карты: Map < Key, Val > strongCache = Maps.newHashMap (cache); –

+0

извините за < мусор; нет предварительного просмотра, поэтому я должен был догадаться, удастся ли мне бежать или нет. :( –

0

Кэши имеют тенденцию быть проблематичными. IIRC, в Sun JRE есть SoftCache, у которого было много проблем.

В любом случае, самое простое - использовать SoftReference с на карте. Это должно работать нормально, пока накладные расходы SoftReference плюс Map.Entry значительно ниже, чем кэшированные данные.

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

«Блокировка» карты, вы, вероятно, захотите избежать, если необходимо. Вам понадобятся ссылки на все данные (и выселение, если не null). Это будет уродливо.

+0

Для блокировки, я думал, что могу просто скопировать все это в обычную HashMap – sanity

+0

Ну, вы можете скопировать содержимое карты, но тогда вам также придется разыгрывать мягкие ссылки. Хотя копирование вместо блокировки - лучший способ пойти. –

0

Я бы настоятельно рекомендовал использовать что-то вроде Ehcache вместо того, чтобы повторно изобретать систему кеширования. Он очень прост в использовании, очень настраивается и отлично работает.

0

Как сказал матовый b, что-то вроде Ehcache или JbossCache - хороший первый шаг.

Если вы хотите что-то легкое и незавершенное, посмотрите на коллекции google. Например, вы можете использовать MapMaker (http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/collect/BiMap.html), чтобы создать карту с мягкими/слабыми ключами и значениями, поэтому она будет кэшировать только те элементы, в которых у нее есть место (хотя вы не получите LRU).

+0

Упс - лучшая ссылка: http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/MapMaker.html – nojo

0

Я была такая же потребность в прошлом, и это, как я реализовал свой кэш:

  • есть менеджер кэш-память, которая имеет минимальный и максимальный предел памяти (макс ограничить это важно в любом случае)
  • каждый зарегистрированный кэш имеет следующие (важные) параметры: максимальная мощность (в большинстве случаев у вас есть более высокий предел, вы не хотите идти держать больше, чем Х пунктов) использование памяти & процентов
  • Я использую LinkedHashMap и ReentrantReadWriteLock для защиты кеша.
  • каждые X puts Я вычисляю среднее потребление памяти на запись и выключение триггера (асинхронный), если вычисленный предел памяти> допустимый предел памяти.
  • Конечно, вычисление памяти фактически не показывает реальное потребление памяти, но сравнивает вычисленную память с реальным значением (используя профилировщик). Я считаю, что он достаточно близко.

Я планировал также поставить дополнительную защиту в кеш, чтобы выселить в случае, если puts идут быстрее, чем выселения на основе памяти, но до сих пор я не нашел нужды делать это.