Арена и сегменты памяти MemorySegment

Последнее обновление: 21.12.2025

Арена

Одним из ключевых компонентов при взаимодействии с нативными (внешними) функциями является арена (arena). При обращении к нативным (внешним) функциями код Java должен работать с памятью вне кучи (хипа), то есть с памятью, которая не была выделена с помощью оператора new. И арена как раз управляет жизненным циклом сегментов памяти вне кучи, позволяя легко выделять и автоматически освобождать используемую память.

Создание и закрытие арены

На платформе Java арена представлена классом Arena. Для создания арены применяется один из статических методов класса:

  • global(): возвращает "глобальную" арену. Сегменты памяти, выделенные в рамках глобальной арены, доступны любому потоку.

  • ofAuto(): cоздает новую арену, которая автоматически управляется сборщиком мусора. Сегменты арены доступны любому потоку

  • ofConfined(): возвращает новую "ограниченную" арену. Сегменты, выделенные для ограниченной арены, доступны потоку, который создал арену, то есть потоку-владельцу арены.

  • ofShared(): возвращает новую общую арену. Сегменты, выделенные для общей арены, доступны любому потоку.

Для закрытия арены предназначен метод close(). Но в зависимости от типа арены принцип закрытия может различаться:

  • "Ограниченная" арена (ofConfined()) должна быть закрыта в том же потоке, в котором она была создана. Иначе будет выброшено исключение.

  • "Глобальная" арена (global()) никогда не закрывается, и выделенные ею сегменты памяти никогда не освобождаются.

  • "Автоматическую" арену (ofAuto()) закрыть нельзя. Сборщик мусора сам в конечном итоге освободит сегменты памяти, когда они, как и арена, перестанут использоваться. Это удобно, но менее предсказуемо.

  • "Общая" арена (ofShared()) работает до тех пор, пока не будет вызван метод close в любом потоке

Таким образом, явно вызывать метод close() необходимо только для ограниченной и общей арен, а для глобальной и автоматической вызов метода close() сгенерирует исключение.

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

Arena arena = Arena.ofConfined();   // создаем арену
// неокторая работа с ареной
arena.close();  // закрываем арену

Но поскольку класс арены реализует интерфейс AutoCloseable (в виде метода close()), то арену удобно создавать и использовать через конструкцию try-с ресурсами, которая автоматически освобождает память арены:

try (Arena arena = Arena.ofConfined()) {

    // работа с ареной

} // Арена закрыта

Сегменты памяти MemorySegment

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

Для использования с внешними/нативными функциями выделяются сегменты памяти вне кучи.

Для выделения памяти у класса Arena есть ряд методов (в основном реализаций интерфейса java.lang.foreign.SegmentAllocator). Некоторые из них:

  • allocate(long byteSize, long byteAlignment)

    Этот метод принимает два параметра:

    • byteSize: размер выделяемой памяти в байтах

    • byteAlignment: выравнивание памяти в байтах

    Метод возвращает сегмент памяти заданного размера (первый параметр), адрес которого выравнивается исходя из значения второго параметра.

  • allocateFrom(String str)

    Преобразует строку Java в строку C, завершающуюся нулевым символом, используя кодировку UTF-8, и сохраняет результат в сегмент памяти.

  • allocateFrom(ValueLayout.ofType elementLayout, Type[] elements)

    Выделяет память для хранения элементов заданного примитивного типа Type (это может быть int, float и т.д.).

  • allocateFrom(ValueLayout.of[Type] elementLayout, Type element)

    Аналогичен предыдущему варианту, только выделяет память для хранения одного элемента element заданного примитивного типа Type и сохраняет значение element в выделенной памяти

  • allocate(MemoryLayout layout)

    Выделяет память вне кучи с заданным макетом layout.

  • allocate(MemoryLayout layout, long count)

    Выделяет память вне кучи с заданным макетом layout с количеством элементов равным count

Все эти методы возвращают объект типа MemorySegment. Пример применения метода allocate():

import java.lang.foreign.*;

public class Program {

    public static void main(String[] args) {

        try (Arena arena = Arena.ofConfined()) {

            // выделяем сегмент памяти - в 16 байт с выравниванием по 8 байтам
            MemorySegment segment1 = arena.allocate(16, 8);
            // можем выделить еще один сегмент
            MemorySegment segment2 = arena.allocate(32, 4);

            // просто выведем информацию на экран
            System.out.println(segment1);   // MemorySegment{ kind: native, address: 0x772c881780e0, byteSize: 16 }
            System.out.println(segment2);   // MemorySegment{ kind: native, address: 0x772c881783d0, byteSize: 32 }
        } 
    }
}

Метод allocateFrom() является наиболее простым способом для сохранения в сегменте памяти вне кучи какой-нибудь строки:

import java.lang.foreign.*;

public class Program {

    public static void main(String[] args) {

        try (Arena arena = Arena.ofConfined()) {

            String str = "Hello METANIT.COM";

            MemorySegment str_seg = arena.allocateFrom(str);

            System.out.println(str_seg);   // MemorySegment{ kind: native, address: 0x7aa79c1802d0, byteSize: 18 }
        } 
    }
}

Здесь происходит кодирование строки Java в UTF-8 и выделение сегмент памяти, который содержит байты строки, включая нулевой терминатор.

Осовобождение памяти

Для освобождения сегмента памяти в ограниченной или общей арене необходимо закрыть эту арену с помощью метода close().

В автоматической арене для освобождения сегмента сегменту передается значение null, и затем в какой-то момент времени сборщик мусора освободит память:

MemorySegment segment = Arena.ofAuto().allocate(...);

...
segment = null; // Память в конечном итоге освобождается

Наконец, сегменты памяти глобальной арены в процессе работы программы никогда не освобождаются.

Стоит также отметить, что освободить отдельные сегменты памяти невозможно.

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850
Morty Proxy This is a proxified and sanitized view of the page, visit original site.