#java #memory #garbage-collection #bytebuffer #terracotta
#java #память #сборка мусора #bytebuffer #terracotta
Вопрос:
Я изучаю варианты, которые помогут моему приложению, требующему много памяти, и при этом я наткнулся на BigMemory от Terracotta. Насколько я понимаю, они используют преимущества «собственной памяти», не собираемой без мусора, вне кучи, и, по-видимому, это примерно в 10 раз медленнее, чем хранилище кучи, из-за проблем с сериализацией / десериализацией. До прочтения о BigMemory я никогда не слышал о «встроенной памяти» за пределами обычного JNI. Хотя BigMemory — интересный вариант, который заслуживает дальнейшего рассмотрения, я заинтригован тем, чего можно было бы достичь с помощью встроенной памяти, если бы проблему сериализации можно было обойти.
Является ли встроенная память Java быстрее (я думаю, это влечет за собой ByteBuffer
объекты?) чем традиционная память кучи, когда нет проблем с сериализацией (например, если я сравниваю ее с огромным byte[]
)? Или выполнять капризы сборки мусора и т.д. сделать этот вопрос неопровержимым? Я знаю, что «измерить это» — это распространенный ответ здесь, но, боюсь, я бы не стал настраивать репрезентативный тест, поскольку я еще недостаточно знаю о том, как работает встроенная память в Java.
Комментарии:
1. Современные компиляторы «точно в срок» преобразуют использование кучи в использование собственного стека, когда это возможно в любом случае. Было бы действительно сложно даже протестировать ее и сказать «Это java heap, это native», не заставляя ее запускаться в интерпретаторе байтового кода.
Ответ №1:
Прямая память быстрее при выполнении ввода-вывода, потому что она позволяет избежать одной копии данных. Однако для 95% приложений вы не заметите разницы.
Вы можете хранить данные в прямой памяти, однако это будет не быстрее, чем хранение данных POJOS. (или такой же безопасной, читаемой или поддерживаемой) Если вы беспокоитесь о GC, попробуйте создать свои объекты (должны быть изменяемыми) заранее и повторно использовать их, не отбрасывая. Если вы не удалите свои объекты, собирать будет нечего.
Является ли встроенная память Java быстрее (я думаю, это влечет за собой объекты ByteBuffer?) чем традиционная память кучи, когда нет проблем с сериализацией (например, если я сравниваю ее с огромным byte [])?
Прямая память может быть быстрее, чем использование байта [], если вы используете use non bytes like int
, поскольку она может считывать / записывать целых четыре байта, не превращая данные в байты. Однако это медленнее, чем использование POJOs, поскольку оно должно проверять границы каждого доступа.
Или выполнять капризы сборки мусора и т.д. сделать этот вопрос неопровержимым?
Скорость не имеет ничего общего с GC. GC имеет значение только при создании или отбрасывании объектов.
Кстати: Если вы сведете к минимуму количество объектов, которые вы отбрасываете, и увеличите размер Eden, вы можете предотвратить даже незначительный сбор данных в течение длительного времени, например, целого дня.
Комментарии:
1. «попробуйте создать свои объекты (должны быть изменяемыми) заранее и повторно использовать их, не отбрасывая». Одна из лучших практик в Java — не иметь пулов объектов, почему вы это сказали?
2. @Peter Lawrey Действительно ли GC не представляет проблемы? Для выполнения всего в куче требуется большая куча, что, я полагаю, требует большего управления со стороны GC. Я думаю, что работа с встроенной памятью означает, что GC может патрулировать гораздо меньшую кучу; если я не ошибаюсь.
3. @pih Я не уверен, что я бы сказал, что лучше всего не иметь пулов объектов, но они создают некоторые риски для ошибок, если объекты не очищаются должным образом и не сбрасываются между использованием. Я считаю, что их следует использовать по мере необходимости, а большие буферы ввода-вывода и т.д. Часто можно использовать повторно, а не перераспределять память для них и создавать их по требованию.
4. @Michael В generational GC GC очищает поколение за поколением. Как только объекты перемещаются в выделенное пространство, они не оказывают никакого влияния на молодое поколение. (На самом деле используется другой GC)
5. @sqawknull, я бы согласился, что повторное использование объектов более подвержено ошибкам, чем удаление объектов, и должно использоваться как приемлемое для вашей системы. Возможно, этого вообще не происходит, или это может быть сердце вашей системы. Я хотел прояснить, что это вариант, и использование прямых буферов не единственное. (Фактически прямые буферы, возможно, более подвержены ошибкам, поскольку у вас нет проверки типа)
Ответ №2:
Смысл BigMemory не в том, что встроенная память быстрее, а скорее в том, чтобы уменьшить накладные расходы сборщика мусора, требующего усилий по отслеживанию ссылок на память и ее очистке. По мере увеличения размера вашей кучи увеличиваются и ваши интервалы GC и загрузка процессора. В зависимости от ситуации это может создать своего рода «стеклянный потолок», когда куча Java становится настолько большой, что GC превращается в «борова», потребляющего огромное количество процессорной мощности при каждом запуске GC. Кроме того, многие алгоритмы GC требуют определенного уровня блокировки, что означает, что никто ничего не может сделать, пока эта часть алгоритма отслеживания ссылок GC не завершится, хотя многие JVM намного лучше справляются с этим. Там, где я работаю, с нашим сервером приложений и JVM, мы обнаружили, что «стеклянный потолок» составляет около 1,5 ГБ. Если мы попытаемся настроить кучу больше, чем это, процедура GC начнет потреблять более 50% общего процессорного времени, так что это очень реальные затраты. Мы определили это с помощью различных форм GC-анализа, предоставляемых нашим поставщиком JVM.
BigMemory, с другой стороны, использует более ручной подход к управлению памятью. Это уменьшает накладные расходы и как бы возвращает нас к необходимости выполнять нашу собственную очистку памяти, как мы делали в C, хотя и в гораздо более простом подходе, похожем на HashMap. Это по существу устраняет необходимость в традиционной процедуре сборки мусора, и в результате мы устраняем эти накладные расходы. Я полагаю, что люди из Terracotta использовали встроенную память через ByteBuffer, поскольку это простой способ выйти из-под Java garbage collector.
Следующий технический документ содержит некоторую полезную информацию о том, как они спроектировали BigMemory, и некоторую информацию о накладных расходах GC: http://www.terracotta.org/resources/whitepapers/bigmemory-whitepaper.
Комментарии:
1. Обратите внимание, что это значение «стеклянного потолка» сильно зависит от приложения. В основном мы используем от 50 до 80 гигабайт в качестве размера кучи и не видим каких-либо значительных накладных расходов на сборку по сравнению с меньшими настройками с 20-30 гигабайтами.
Ответ №3:
Я заинтригован тем, чего можно было бы достичь с помощью встроенной памяти, если бы проблему сериализации можно было обойти.
Я думаю, что ваш вопрос основан на ложном предположении. AFAIK, невозможно обойти проблему сериализации, о которой они здесь говорят. Единственное, что вы могли бы сделать, это упростить объекты, которые вы помещаете в BigMemory, и использовать пользовательский код сериализации / десериализации, чтобы уменьшить накладные расходы.
Хотя тесты могут дать вам приблизительное представление о накладных расходах, фактические накладные расходы будут сильно зависеть от приложения. Мой совет был бы:
-
Идите по этому маршруту, только если вы знаете, что вам это нужно. (Вы будете привязывать свое приложение к определенной технологии реализации.)
-
Будьте готовы к некоторым навязчивым изменениям в вашем приложении, если задействованные данные еще не обрабатываются с использованием в качестве кэша.
-
Будьте готовы потратить некоторое время на (повторную) настройку вашего кода кэширования, чтобы получить хорошую производительность с BigMemory.
-
Если ваши структуры данных сложны, ожидайте пропорционально больших затрат времени выполнения и усилий по настройке.