Компоновка памяти MemoryLayout и работа с MemorySegment

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

Ряд методов создания сегментов памяти в классе Arena принимают в качестве параметра объект типа MemoryLayout. Этот интерфейс описывает компоновку памяти - размер сегмента памяти, выравнивание, порядок байтов. Интерфейс ValueLayout (который наследуется от MemoryLayout) предоставляет ряд констант, которые представляют встроенную компоновку памяти для определенного типа данных:

  • ADDRESS: константа компоновки адреса, размер которой совпадает с размером машинного адреса (size_t в языке С), выравнивание по байтам установлено равным sizeof(size_t)

  • ADDRESS_UNALIGNED: константа компоновки адреса без выравнивания, размер которой совпадает с размером машинного адреса (size_t)

  • JAVA_BOOLEAN: константа компоновки значения, размер которой совпадает с размером типа boolean в Java, выравнивание по байтам установлено равным 1

  • JAVA_BYTE: константа компоновки значения, размер которой совпадает с размером типа byte в Java, выравнивание по байтам установлено на 1

  • JAVA_CHAR: константа компоновки значения, размер которой совпадает с размером типа char в Java, выравнивание по байтам установлено на 2

  • JAVA_CHAR_UNALIGNED: константа компоновки адреса без выравнивания, размер которой совпадает с размером типа char в Java

  • JAVA_DOUBLE: константа компоновки значений, размер которой совпадает с размером типа double в Java, выравнивание по байтам установлено на 8

  • JAVA_DOUBLE_UNALIGNED: константа компоновки адреса без выравнивания, размер которой совпадает с размером типа double в Java

  • JAVA_FLOAT: константа компоновки значений, размер которой совпадает с размером типа float в Java, выравнивание по байтам установлено на 4

  • JAVA_FLOAT_UNALIGNED: константа компоновки адреса без выравнивания, размер которой совпадает с размером типа float в Java

  • JAVA_INT: константа расположения значений, размер которой совпадает с размером типа int в Java, выравнивание байтов установлено на 4

  • JAVA_INT_UNALIGNED: константа компоновки адреса без выравнивания, размер которой совпадает с размером типа int в Java

  • JAVA_LONG: константа компоновки значений, размер которой совпадает с размером типа long в Java, выравнивание по байтам установлено на 8

  • JAVA_LONG_UNALIGNED: константа компоновки адреса без выравнивания, размер которой совпадает с размером типа long в Java

  • JAVA_SHORT: константа компоновки значений, размер которой совпадает с размером типа short в Java, выравнивание по байтам установлено на 2

  • JAVA_SHORT_UNALIGNED: константа компоновки адреса без выравнивания, размер которой совпадает с размером типа short в Java

Для выделения массива необходимо указать структуру элементов и количество элементов. Для этого можно использовать константы компоновки памяти, определенные в классе ValueLayout. Например, выделим массив значений типа double:

import java.lang.foreign.*;

public class Program {

    public static void main(String[] args) {

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

            // выделяем память для 16 значений double
            MemorySegment double_seg = arena.allocate(ValueLayout.JAVA_DOUBLE, 16);

            System.out.println(double_seg);   // MemorySegment{ kind: native, address: 0x7477a8184cb0, byteSize: 128 }
        } 
    }
}

Если у нас уже есть значения в массиве, можно использовать метод allocateFrom():

import java.lang.foreign.*;

public class Program {

    public static void main(String[] args) {

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

            double[] nums = {1.0, 2.0, 3.0, 4.0};
            // выделяем память для массива nums
            MemorySegment double_seg = arena.allocateFrom(ValueLayout.JAVA_DOUBLE, nums);

            System.out.println(double_seg);   // MemorySegment{ kind: native, address: 0x75f5cc1851a0, byteSize: 32 }
        } 
    }
}

Работа с сегментами памяти

Интерфейс MemorySegment предоставляет множество методов для различных операций с данными в памяти. Рассмотрим некоторые из них:

  • String getString(long offset)

    преобразует строку UTF-8 в строку Java.

  • type getAtIndex(ValueLayout.oftype layout, long index)

    считывает значение примитивного типа по заданному индексу массива.

  • void setAtIndex(ValueLayout.oftype layout, long index, type value)

    записывает значение примитивного типа по заданному индексу массива.

  • type get(ValueLayout.oftype layout, long offset)

    считывает значение примитивного типа по заданному смещению.

  • void set(ValueLayout.oftype layout, long offset, type value)

    записывает значение примитивного типа по заданному смещению.

  • type[] toArray(ValueLayout.oftype elementLayout)

    копирует содержимое сегмента памяти в массив примитивного типа type.

Например, возьмем метод getString(). Предположим, что функция C заполнила сегмент памяти данными. Чтобы преобразовать строку UTF-8 в строку Java, в метод getString() передается смещение 0:

import java.lang.foreign.*;

public class Program {

    public static void main(String[] args) {

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

            String str = "Hello METANIT.COM";

            // преобразуем строку Java в строку UTF-8
            MemorySegment str_seg = arena.allocateFrom(str);

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

            // обратное преобразование в строку Java
            String java_str = str_seg.getString(0);

            System.out.println(java_str);   // Hello METANIT.COM
        } 
    }
}

Для чтения или записи по индексу массива указывается структура памяти и индекс:

import java.lang.foreign.*;

public class Program {

    public static void main(String[] args) {

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

            double[] nums = {1.0, 2.0, 3.0, 4.0};
            // выделяем память для массива nums
            MemorySegment double_seg = arena.allocateFrom(ValueLayout.JAVA_DOUBLE, nums);

            // устанавливаем для элемента с индексом 2 значение 5.0
            int index = 2;
            double_seg.setAtIndex(ValueLayout.JAVA_DOUBLE, index, 5.0);
            // получаем один элемент double по индексу 2
            double second_double = double_seg.getAtIndex(ValueLayout.JAVA_DOUBLE, index);

            System.out.println(second_double);  // 5.0
        } 
    }
}

Обратите внимание, что сегмент не сохраняет никакой информации о структуре данных внутри. Вам необходимо указывать структуру данных (в данном случае ValueLayout.JAVA_DOUBLE) при каждом доступе к элементам или при их обновлении.

Также можно получить массив Java со всеми значениями:

double[] double_nums = double_seg.toArray(ValueLayout.JAVA_DOUBLE);

Методы get()/set() аналогичны методам getAtIndex/setAtIndex, но принимают смещение в байтах:

import java.lang.foreign.*;

public class Program {

    public static void main(String[] args) {

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

            double[] nums = {1.0, 2.0, 3.0, 4.0};
            // выделяем память для массива nums
            MemorySegment double_seg = arena.allocateFrom(ValueLayout.JAVA_DOUBLE, nums);

            // устанавливаем значение 11 по смещению 8 байт
            double_seg.set(ValueLayout.JAVA_DOUBLE, 8, 11);
            // получаем один элемент double по смещению 16 байт
            double second_double = double_seg.get(ValueLayout.JAVA_DOUBLE, 16);

            System.out.println(second_double);  // 3.0
        } 
    }
}
Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850