ByteBuffer.allocate() и ByteBuffer.allocateDirect()

К allocate() или к allocateDirect(), вот в чем вопрос.

В течение нескольких лет я просто придерживался мысли о том, что поскольку DirectByteBuffer является прямым отображением памяти на уровне ОС, он будет быстрее выполнять вызовы get/put, чем HeapByteBuffer s. Я никогда не интересовался точной информацией о ситуации до сих пор. Я хочу знать, какой из двух типов ByteBuffer быстрее и на каких условиях.

Ответ 1

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

Операционные системы выполняют ввод/вывод операции над областями памяти. Эти области памяти, поскольку системы, являются смежными последовательности байтов. Это не удивительно то только байтовые буферы право участвовать в I/O операции. Также напомним, что операционная система будет напрямую получать доступ адресное пространство процесса, в в этом случае процесс JVM для передачи данные. Это означает, что области памяти которые являются мишенями для операций ввода-вывода, должны быть смежными последовательностями байтов. В JVM, массив байтов может не быть хранятся в памяти в памяти или Сборщик мусора может переместить его на любой время. Массивы являются объектами в Java и способ хранения данных внутри объект может варьироваться от одной JVM к другому.

По этой причине понятие был введен прямой буфер. непосредственный буферы предназначены для взаимодействия с каналами и обычными процедурами ввода-вывода. Они прилагают максимум усилий для байтов в области памяти, что канал может использоваться для прямого или необработанного, доступ с помощью собственного кода для операционная система для слива или заполнения область памяти напрямую.

Прямые байтовые буферы, как правило, лучший выбор для операций ввода-вывода. От дизайна, они поддерживают большинство эффективный механизм ввода-вывода, доступный для JVM. Небедовыми байтовыми буферами могут быть передается по каналам, но это может несут штраф за исполнение. Это обычно невозможен буфера, чтобы стать объектом Операция ввода-вывода. Если вы передаете недровод ByteBuffer указывает на канал для напишите, канал может косвенно делать для каждого вызова:

  • Создайте временный прямой ByteBuffer объект.
  • Скопировать содержимое непрямого буфер во временный буфер.
  • Выполните операцию ввода-вывода низкого уровня используя временный буфер.
  • Временный буферный объект отключается объема и в конечном итоге мусор собраны.

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

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

Компромисс производительности при использовании Прямые или непрямые буферы могут широко варьируются по JVM, операционной системе, и дизайн кода. Выделяя память вне кучи, вы можете подвергнуть свою применение дополнительных сил которые JVM не знает. когда приведение дополнительных движущихся частей в играйте, убедитесь, что вы достигаете желаемый эффект. Я рекомендую старое программное обеспечение maxim: сначала сделайте это работайте, а затем сделайте это быстро. Не волнуйся слишком много о оптимизации перед фронтом; сначала сконцентрируйтесь на правильности. Внедрение JVM может выполнять кэширование буфера или другое оптимизация, которая даст вам вам нужно без большого количества ненужные усилия с вашей стороны.

Ответ 2

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

Ответ 3

Лучше всего делать собственные измерения. По-видимому, быстрый ответ заключается в том, что отправка из буфера allocateDirect() занимает от 25% до 75% меньше времени, чем вариант allocate() (проверяется как копирование файла на /dev/null ), в зависимости от размера, но что само распределение может быть значительно медленнее (даже в 100 раз).

Источники:

Ответ 4

поскольку DirectByteBuffers являются прямым отображение памяти на уровне ОС

Это не так. Это просто нормальная память приложений, но не подвержена перемещению во время Java GC, что значительно упрощает работу внутри уровня JNI. То, что вы описали, относится к MappedByteBuffer.

чтобы он быстрее выполнял вызовы get/put

Вывод не вытекает из предпосылки; посылка является ложной; и вывод также неверен. Они быстрее, когда вы попадаете в слой JNI, и если вы читаете и пишите из одного и того же DirectByteBuffer, они намного быстрее, потому что данные никогда не должны пересекать границу JNI вообще.