Ehcache переговоры о на кучи и кучи памяти. В чем разница? Какие Арги JVM используются для их настройки?

автор: Synesso

6 ответов

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

внутренние детали, связанные с управлением и использованием вне кучи магазин не очень очевиден в ссылке, размещенной в вопросе, поэтому было бы разумно проверить детали Терракотовый BigMemory, который используется для управления хранилищем вне диска. BigMemory (хранилище вне кучи) должно использоваться, чтобы избежать накладных расходов GC в куче, которая составляет несколько мегабайт или гигабайт. BigMemory использует адресное пространство памяти процесса JVM через прямой ByteBuffers которые не подлежат GC в отличие от других собственных объектов Java.

Владимир Иванов — Native код, Off-heap данные и Java

автор: Vineet Reynolds

Что такое выгрузка кучи ?

обычно все временные объекты, которые вы выделяете, управляются сборщиком мусора java. Хотя виртуальная машина выполняет приличную работу по сбору мусора, в определенный момент виртуальная машина должна сделать так называемый «полный GC». Полный GC включает сканирование полной выделенной кучи, что означает, что паузы/замедления GC пропорциональны размер кучи приложений.

Поэтому не доверяйте никому, кто говорит вам: «память дешева». В java потребление памяти снижает производительность. Кроме того, вы можете получить заметные паузы, используя размеры кучи > 1 Гб. Это может быть неприятно, если у вас есть какие-либо вещи, близкие к реальному времени, в кластере или сетке процесс java может перестать отвечать и быть удален из кластера.

однако сегодняшние серверные приложения (часто построенные поверх раздутых фреймворков ; -)) легко требуют кучи далеко за пределами 4гб.

одним из решений этих требований к памяти является «разгрузка» частей объектов в кучу, отличную от java (непосредственно выделенную из ОС). К счастью, Ява.nio предоставляет классы для прямого выделения / чтения и записи «неуправляемых» кусков памяти (даже сопоставленных с памятью файлов).

таким образом, можно выделить большое количество «неуправляемой» памяти и использовать ее для сохранения объектов. Чтобы сохранить произвольные объекты в неуправляемой памяти, наиболее жизнеспособным решением является использование Сериализация. Это означает, что приложение сериализует объекты в память offheap, позже объект можно прочитать с помощью десериализации.

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

понятно, что производительность такого буфера кучи зависит в основном от производительности реализации сериализации. Хорошие новости: по какой-то причине FST-сериализация происходит довольно быстро :-).

Владимир Иванов — Native код, Off-heap данные и Java

  • кэш сеанса в серверном приложении. Используйте сопоставленный файл памяти для хранения гигабайт (неактивных) сеансов пользователя. После входа пользователя в приложение можно быстро получить доступ к данным, связанным с пользователем, без необходимости работать с базой данных.
  • кэширование результатов вычислений (запросы, html-страницы. ) (только если расчет идет медленнее, чем десериализации объекта result ОФК).
  • очень простой и быстрый persistance с помощью памяти сопоставленных файлов
Читайте также:  Как уменьшить в Майнкрафте экран

Edit: для некоторых сценариев можно выбрать более сложные алгоритмы сбора мусора, такие как ConcurrentMarkAndSweep или G1 для поддержки больших куч (но это также имеет свои пределы за пределами кучи 16GB). Существует также коммерческая СПМ с улучшенным «беспаузным» GC (Azul).

автор: R.Moeller

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

хранилище Ehcache вне кучи удаляет ваш обычный объект из кучи, сериализует его и сохраняет его в виде байтов в куске памяти что EHCache управляет. Это как хранить его на диске, но он все еще в ОЗУ. Объекты не используются непосредственно в этом состоянии, они должны быть десериализованы в первую очередь. Также не подлежит сбору мусора.

Источник: askdev.ru

Использование разделяемой памяти в Java и off-heap кеширование

На прошлой неделе состоялся успешный эксперимент по запуску нового решения для download-сервиса. Один достаточно скромный сервер (2 x Intel Xeon E5620, 64 GB RAM) под управлением Java-приложения собственной разработки принял на себя нагрузку восьми Tomcat’ов, обслуживая более 70 тысяч HTTP-запросов в секунду общей пропускной способностью 3000 Mb/s. Таким образом, весь трафик Одноклассников, связанный с пользовательскими смайликами, обрабатывался одним сервером.

Вполне естественно, что высокие нагрузки требовали нестандартных решений. В цикле статей о разработке высоконагруженного сервера на Java я расскажу о проблемах, с которыми нам пришлось столкнуться, и о том, как мы их преодолели. Сегодня речь пойдет о кешировании изображений вне Java Heap и об использовании Shared Memory в Java.

Кеширование

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

  • 64-bit keys, byte array values: идентификатор изображения — целое число типа long, а данные — картинка в формате PNG, GIF или JPG со средним размером 4 KB;
  • In-process, in-memory: для максимальной скорости доступа все данные — в памяти процесса;
  • RAM utilization: под кеш выделяется вся доступная оперативная память;
  • Off-heap: 50 GB данных разместить в Java Heap было бы проблематично;
  • LRU или FIFO: устаревшие ключи могут вытесняться более новыми;
  • Concurrency: одновременное использование кеша в сотне потоков;
  • Persistence: приложение может быть перезапущено с сохранением уже закешированных данных.

Shared Memory

В Linux объекты Shared Memory реализованы посредством специальной файловой системы, монтируемой к /dev/shm . Так, например, POSIX функция shm_open(«name», . ) эквивалентна системному вызову open(«/dev/shm/name», . ) . Таким образом, в Java мы можем работать с разделяемой памятью Linux как с обычными файлами. Следующий фрагмент кода откроет объект разделяемой памяти с именем image-cache размером 1 GB. Если объекта с таким именем не существует, будет создан новый. Важно, что после завершения приложения объект останется в памяти и будет доступен при следующем запуске.

Читайте также:  Как делать порталы в дивайн рпг Майнкрафт

RandomAccessFile f = new RandomAccessFile(«/dev/shm/image-cache», «rw»); f.setLength(1024*1024*1024L);

Теперь созданный объект-файл надо отобразить в адресное пространство процесса и получить адрес этого участка памяти.

Способ 1. Легальный, но неполноценный

Воспользуемся Java NIO API:

RandomAccessFile f = . MappedByteBuffer buffer = f.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, f.length());

Самый главный недостаток этого метода заключается в том, что нельзя отображать файлы размером более 2 GB, что и описано в Javadoc к методу map: The size of the region to be mapped; must be non-negative and no greater than Integer.MAX_VALUE.

Работать с полученным участком памяти можно либо стандартными методами ByteBuffer’а, либо напрямую через Unsafe, вытащив адрес памяти с помощью Reflection:

public static long getByteBufferAddress(ByteBuffer buffer) throws Exception

Публично доступного метода unmap у такого MappedByteBuffer’а нет, однако есть полу-легальный способ освободить память без вызова GC:

((sun.nio.ch.DirectBuffer) buffer).cleaner().clean();

Способ 2. Полностью на Java, но с использованием «тайных знаний»

В Oracle JDK есть класс sun.nio.ch.FileChannelImpl с приватными методами map0 и unmap0 , которые лишены ограничения в 2 GB. map0 возвращает непосредственно адрес «замапленного» участка, что для нас даже удобнее, если мы используем Unsafe.

Method map0 = FileChannelImpl.class.getDeclaredMethod( «map0», int.class, long.class, long.class); map0.setAccessible(true); long addr = (Long) map0.invoke(f.getChannel(), 1, 0L, f.length()); Method unmap0 = FileChannelImpl.class.getDeclaredMethod( «unmap0», long.class, long.class); unmap0.setAccessible(true); unmap0.invoke(null, addr, length);

Такой механизм будет работать как в Linux, так и под Windows. Единственный его недостаток — отсутствие возможности выбора конкретного адреса, куда будет «замаплен» файл.

Необходимость в этом может возникнуть, если в кеше присутствуют абсолютные ссылки на адреса памяти внутри этого же кеша: такие ссылки станут невалидными, если отобразить файл по другому адресу. Выхода два: либо хранить относительные ссылки в виде смещения относительно начала файла, либо прибегнуть к вызову нативного кода через JNI или JNA. Системные вызовы mmap в Linux и MapViewOfFileEx в Windows позволяют задать предпочитаемый адрес, куда «замапить» файл.

Алгоритм кеширования

Ключевым для производительности кеша, да и download-сервера в целом, является алгоритм поиска в кеше, т.е. метод get . Метод put в нашем сценарии вызывается значительно реже, но тоже не должен быть слишком медленным. Хочу представить наше решение для быстрого потокобезопасного FIFO кеша в непрерывной области памяти фиксированного размера.

Вся память разделяется на сегменты одинакового размера — корзины хеш-таблицы, по которым равномерно распределяются ключи. В самом простом виде

Segment s = segments[key % segments.length];

Сегментов может быть много — несколько тысяч. Каждому из них сопоставляется ReadWriteLock . Одновременно с сегментом может работать либо неограниченное количество читателей, либо только один писатель.

Интересная деталь: использование стандартных ReentrantReadWriteLock’ов привело к потере 2 GB в Java Heap. Как оказалось, в JDK 6 существует ошибка, приводящая к чрезмерному потреблению памяти таблицами ThreadLocal в реализации ReentrantReadWriteLock . Хотя в JDK 7 ошибка уже исправлена, в нашем случае мы заменили прожорливый Lock на Semaphore . Кстати, вот вам и маленькое упражнение:
как реализовать ReadWriteLock при помощи Semaphore?

Итак, сегмент. Он состоит из области индекса и области данных. Индекс представляет собой упорядоченный массив из 256 ключей, сразу за которым идет такой же длины массив из 256 ссылок на значения. Ссылка задает смещение внутри сегмента на начало блока данных и длину этого блока в байтах.

Читайте также:  Как работает поезд в Майнкрафт

Блоки данных, то есть, собственно сами изображения, выравнены по восьмибайтовой границе для оптимального копирования. Сегмент также хранит количество ключей в нем и адрес следующего блока данных для метода put . Новые блоки записываются друг за другом по принципу циклического буфера. Как только место в сегменте кончается, происходит запись с начала сегмента поверх более ранних данных.

  1. по хешу ключа вычисляется сегмент, в котором будет производиться поиск;
  2. в области индекса бинарным поиском ищется ключ;
  3. если ключ найден, из массива ссылок достается смещение, по которому располагаются данные.
  1. по хешу ключа вычисляется сегмент;
  2. считывается адрес очередного блока данных и вычисляется адрес следующего блока путём прибавления размера записываемого объекта с учетом выравнивания;
  3. если сегмент заполнен, линейным поиском по массиву ссылок находятся и удаляются из индекса ключи, чьи данные будут перезаписаны очередным блоком;
  4. значение, представленное байтовым массивом, копируется в область данных;
  5. бинарным поиском находится место в индексе, куда вставляется новый ключ.

Скорость работы

  • put: запись 1 млн. значений размером от 0 до 8 KB каждое;
  • get: поиск по ключу 1 млн. значений;
  • 90% get + 10% put: комбинирование get/put в отношении, приближенном к практическому сценарию использования кеша.

Впрочем, стоит отметить, что описанный алгоритм, будучи предназначенным для решения задачи кеширования изображений, не охватывает многих других сценариев. Например, операции remove и replace , хотя и могут быть легко реализованы, не будут освобождать память, занятую прежними значениями.

Где посмотреть?

Исходные тексты алгоритма кеширования с использованием Shared Memory на github:
https://github.com/odnoklassniki/shared-memory-cache

Где послушать?

На встрече JUG.RU в Санкт-Петербурге, которая состоится 25 июля 2012 г., apangin поделится опытом разработки высоконагруженного сервера на Java, расскажет о характерных проблемах и нетрадиционных приемах.

Что дальше?

В следующих статьях я расскажу, как написать RPC-сервер, обрабатывающий десятки тысяч запросов в секунду, а также поведаю об альтернативном методе сериализации, в разы превосходящем стандартные механизмы Java по производительности и объему трафика. Оставайтесь с нами!

  • Блог компании Одноклассники
  • Высокая производительность
  • Java

Источник: habr.com

Разница между памятью On-heap и Off-heap

Может ли кто-нибудь объяснить разницу между памятью в куче и памятью вне кучи? Отображается ли память вне кучи в размере памяти JVM? Все ли указатели вне кучи?

Shellong 27 Авг 2018 в 10:56

1 ответ

Лучший ответ

Вся память — это собственная память, однако JVM управляет и записывает память в ее куче JVM (не такой, как собственная куча)

Offheap — это термин Java, обозначающий память, не управляемую напрямую. Однако им можно управлять косвенно, используя прямые ByteBuffer (ы) в качестве прокси-объектов для необработанной собственной памяти.

Peter Lawrey 27 Авг 2018 в 11:54

Что означает «неуправляемый напрямую»? Означает ли это, что такой объект не управляется JVM? Это просто указатель в JVM?

Источник: question-it.com