2010-01-28 5 views
24

Мне нужно кэшировать объекты в Java, используя пропорции любой доступной ОЗУ. Я знаю, что другие задали этот вопрос, но ни один из ответов не отвечает моим требованиям.Как эффективно кэшировать объекты в Java с помощью доступной оперативной памяти?

Мои требования:

  • Простой и легкий
  • Не значительно медленнее, чем простой HashMap
  • Использование LRU, или некоторые удаления политики, которая аппроксимирует LRU

Я попытался LinkedHashMap, однако для этого требуется указать максимальное количество элементов, и я не знаю, сколько элементов потребуется для заполнения доступной ОЗУ (их размеры будут значительно различаться).

Мой текущий подход заключается в использовании Картограф Google коллекции следующим образом:

Map<String, Object> cache = new MapMaker().softKeys().makeMap(); 

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

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

+0

какие объекты вы кешируете? Я не совсем понимаю, почему вас беспокоит производительность кеша, учитывая, что, как только вы закончите политику истечения срока действия, вы получите больше накладных расходов, чем простая карта, а EHCache - хорошо развитая кэширующая библиотека, которая (I «Я думаю, что настроен через Spring здесь) не сложно настроить и просто прост в использовании, чем карта. – beny23

+0

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

+0

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

ответ

7

Я получил аналогичные требования к вам - параллелизм (на 2 шестиядерных процессоров) и LRU или аналогичные - и также попробовал Guava MapMaker. Я нашел softValues ​​() намного медленнее, чем weakValues ​​(), но оба делали мое приложение мучительно медленным, когда память заполнялась.

Я пробовал WeakHashMap, и это было менее проблематично, странно даже быстрее, чем использование LinkedHashMap в качестве кэша LRU с помощью метода removeEldestEntry().

Но самым быстрым для меня является ConcurrentLinkedHashMap, который сделал мое приложение на 3-4 (!!) раз быстрее, чем любой другой кэш, который я пробовал. Радость, после дней разочарования! По-видимому, он был включен в MapMaker от Guava, но функция LRU во всяком случае отсутствует в Guava r07. Надеюсь, это сработает для вас.

0

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

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

Большинство решений, которые вы найдете, будут добавлены в дополнение к классам Java, включая EhCache.

Вы посмотрели на commons-collections LRUMap?

Обратите внимание, что для MapMaker существует open issue, чтобы обеспечить функциональность LRU/MRU. Возможно, вы также можете высказать свое мнение там

+0

LRUMap требует, чтобы вы указали максимальный размер карты, но я не знаю, что это будет - я просто хочу, чтобы кеш продолжал расти, чтобы заполнить неиспользуемую RAM. – sanity

0

Используя существующий кеш, храните WeakReference, а не обычные объекты.

Если GC начинает бежать из свободного пространства, значения, удерживаемые WeakReferences, будут освобождены.

+0

будет ли это приблизительно LRU? – beny23

+0

Вы думаете о «SoftReference» - объекты с низкой ссылкой могут быть восстановлены в любое время без учета свободной памяти. – danben

+0

См. Этот разговор о различиях между слабыми и мягкими ссылками: http://crazybobs-talks.googlecode.com/svn/trunk/out/references.pdf и http://www.parleys.com/#st=5&id= 2657 & sl = 53. –

-3

Предполагая, что кеш должен быть потокобезопасным, вам следует изучить пример кеша в книге Брайана Гетца «Java Concurrency in Practice». Я не могу рекомендовать это достаточно высоко.

+0

Это действительно не касалось его вопроса. – danben

+0

@ danben: Я согласен. Но безопасность потоков - это то, что он должен будет рассмотреть. –

4

Я использовал сервлеты для кэширования, и это, вероятно, так же сложно, как реализация нового источника данных или потока, моя рекомендация - использовать jboss-cache или другую хорошо известную кеширующую lib. Таким образом, вы будете хорошо спать без проблем

0

В прошлом я использовал JCS. Вы можете настроить configuration, чтобы попытаться удовлетворить ваши потребности. Я не уверен, что это удовлетворит все ваши требования/потребности, но я нашел, что это довольно эффективно, когда я использовал его.

0

Вы не можете «удалить элементы» можно только прекратить трудно ссылаться на них и ждать GC, чтобы очистить их, чтобы идти с коллекциями Google ...

0

Я не в курсе простой способ узнать размер объекта в Java. Поэтому я не думаю, что вы найдете способ ограничить структуру данных объемом оперативной памяти.

Исходя из этого предположения, вы задерживаетесь, ограничивая его количеством кешированных объектов. Я бы предложил запустить моделирование нескольких сценариев использования в реальном времени и собрать статистику по типам объектов, которые входят в кеш. Затем вы можете рассчитать статистически средний размер и количество объектов, которые вы можете позволить себе кешировать. Несмотря на то, что это всего лишь приблизительное количество оперативной памяти, которую вы хотите выделить в кеш, это может быть достаточно хорошо.

Что касается реализации кэша, то в моем проекте (критическом для производительности приложении) мы используем EhCache, и лично я не считаю его тяжелым.

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

3

Я слышал о таких вещах, как EHCache, но мне кажется, что он очень тяжелый для того, что мне нужно, и я не уверен, достаточно ли он для моего приложения (помня, что решение не может быть значительно медленнее, чем HashMap).

Я действительно не знаю, можно ли сказать, что EHCache является тяжеловесным. По крайней мере, я не рассматриваю EHCache как таковой, особенно при использовании Memory Store (который поддерживается расширенным LinkedHashMap и, конечно же, является самым быстрым вариантом кэширования). Вы должны попробовать.

+0

Это решение имеет ту же проблему, о которой я упоминал в LinkedHashMap, а именно (из документов EHCache). «Все кэши определяют максимальный размер памяти в расчете на количество элементов во время настройки». – sanity

+0

@sanity я вижу ... но если вы не хотите заполнять * всю * доступную память вашими кешированными объектами (и, таким образом, избегать GC), то я не вижу, как это сделать, не ограничивая количество объектов в кеше. Вы делали некоторые репрезентативные тесты, чтобы получить представление о размере в памяти для разных пределов? –

+0

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

1

Я не знаю, было ли это простым решением, особенно по сравнению с EHCache или аналогичным, но посмотрели ли вы на Javolution library? Он не предназначен как таковой, но в пакете javolution.context у них есть шаблон Allocator, который может повторно использовать объекты без необходимости сбора мусора. Таким образом, они сохраняют создание объектов и сборку мусора до минимума, что является важной особенностью для программирования в реальном времени. Возможно, вам стоит взглянуть и попытаться адаптировать его к вашей проблеме.

2

Я считаю, что MapMaker будет единственным разумным способом получить то, о чем вы просите. Если «GC начнет тршевать, а производительность всего приложения резко ухудшится», вы должны потратить некоторое время на правильную настройку различных параметров настройки. Этот документ может показаться немного пугающим первый, но это на самом деле написано очень четко и кладезь полезной информации о GC:

http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf

+0

Я знаком с этим документом, но я ничего не знаю о нем, что решает мою проблему :-( – sanity

0

Кэширование чего-то, SoftReference Возможно, лучший способ до сих пор я могу себе представить.

Или вы можете изобрести объект-пул. Чтобы каждый объект, который вы не используете, вам не нужно его уничтожать. Но это для экономии процессора, а не для сохранения памяти