Собственная и кучная память при передаче через сокеты (и системные вызовы)
Неправда, что для передачи данных из и в сокеты может использоваться только собственная (не куча) память. Он полностью зависит от реализации JVM и может время от времени даже меняться в одной и той же реализации.
На самом деле довольно просто написать функции JNI, которые напрямую используют кучу памяти и избегают копий. JNI API предоставляет методы для нулевого доступа к данным в куче Java:
Второй может быть очень полезен при работе с байтовыми массивами.
Взаимодействие со сборкой мусора
Эти функции JNI могут препятствовать выполнению сборки мусора . Часто бывает выгоднее сделать копию. Это особенно верно при выполнении блокирующего системного вызова (например, чтение из сокета TCP, когда точно не известно, что ядро имеет буферизованные данные для возврата). В других случаях может быть возможно обрабатывать массив постепенно, небольшими порциями, чтобы избежать длинных остановок и необходимости копий.
Из-за этих проблем текущая реализация в OpenJDK 11 не предпринимает попыток передачи с нулевым копированием в и из выделенных в куче (непрямых) байтовых буферов, даже в тех случаях, когда в ядре не будет никакой блокировки и будет не влияет на сборку мусора из-за неограниченных задержек.
Противопоказания для использования прямых байтовых буферов
Использование прямого байтового буфера с NIO имеет разные проблемы: эти буферы требуют своего рода финализации. В результате сборщик мусора не может утилизировать их так же эффективно (и быстро), как другие объекты. Как правило, целесообразно использовать прямые байтовые буферы только в том случае, если они долговечны (например, выделяются вместе с каналом, с которым они используются). Для временных буферов буферы на основе массива (или обычные массивы) в большинстве случаев лучше.
Реализация OpenJDK позволяет избежать этой проблемы, связывая прямые буферы с текущим потоком, прозрачно использует их для передачи по каналам, а затем возвращает их в кэш для каждого потока для будущего использования. Таким образом, прямые буферы не распределяются постоянно и не удаляются.
Старые выпуски OpenJDK
Упомянутые выше функции доступа к массиву критических секций восходят к Java 1.2. Независимо от того, делают ли отдельные виртуальные машины и сборки мусора временные копии, временные копии не определены (интерфейсы были тщательно спроектированы таким образом, что для их реализации не нужно избегать копий). В Hotspot в OpenJDK 8 эти функции JNI никогда не копируют, но, как объяснено в статье Алексея Шипилёва, влияние на сборку мусора варьируется от сборщика к сборщику.