Spec-Zone.ru › OpenJDK 25

Интерфейс MemoryLayout

Все известные подинтерфейсы:
AddressLayout, GroupLayout, PaddingLayout, SequenceLayout, StructLayout, UnionLayout, ValueLayout, ValueLayout.OfBoolean, ValueLayout.OfByte, ValueLayout.OfChar, ValueLayout.OfDouble, ValueLayout.OfFloat, ValueLayout.OfInt, ValueLayout.OfLong, ValueLayout.OfShort
public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, PaddingLayout, ValueLayout
Компоновка памяти описывает содержимое сегмента памяти.

В иерархии компоновок есть два листовых типа: компоновки значений, которые используются для представления значений заданного размера и типа, и компоновки-заполнители, которые, как следует из названия, используются для представления области сегмента памяти, содержимое которой следует игнорировать и которая предназначена главным образом для выравнивания. Некоторые распространённые константы компоновок значений, такие как ValueLayout.JAVA_INT и ValueLayout.JAVA_FLOAT_UNALIGNED, определены в классе ValueLayout. Особый вид компоновки значений — компоновка адреса — используется для моделирования значений, обозначающих адрес области памяти.

Более сложные компоновки можно получить из простых: компоновка последовательности обозначает однородное повторение нуля или более экземпляров компоновки элемента; групповая компоновка обозначает неоднородное объединение нуля или более компоновок-членов. Групповые компоновки бывают двух видов: компоновки структур, в которых компоновки-члены располагаются одна за другой, и компоновки объединений, в которых компоновки-члены располагаются с одним и тем же начальным смещением.

Компоновкам можно при необходимости назначить имя. На имя компоновки можно ссылаться при создании путей компоновки.

Рассмотрим следующее объявление структуры на C:

typedef struct {
    char kind;
    int value;
} TaggedValues[5];
Приведённое выше объявление можно смоделировать с помощью объекта компоновки следующим образом:
SequenceLayout TAGGED_VALUES = MemoryLayout.sequenceLayout(5,
    MemoryLayout.structLayout(
        ValueLayout.JAVA_BYTE.withName("kind"),
        MemoryLayout.paddingLayout(3),
        ValueLayout.JAVA_INT.withName("value")
    )
).withName("TaggedValues");

Характеристики компоновок памяти

У всех компоновок есть размер (выраженный в байтах), который определяется следующим образом:
  • Размер компоновки значения определяется методом ValueLayout.carrier(), связанным с этой компоновкой значения. То есть константа ValueLayout.JAVA_INT имеет тип-переносчик int и размер 4 байта;
  • Размер компоновки адреса зависит от платформы. То есть константа ValueLayout.ADDRESS имеет размер 8 байт на 64-разрядной платформе;
  • Размер компоновки-заполнителя всегда задаётся явно при создании;
  • Размер компоновки последовательности с компоновкой элемента E и количеством элементов L равен размеру E, умноженному на L;
  • Размер компоновки структуры с компоновками-членами M1, M2, ... Mn, размеры которых равны соответственно S1, S2, ... Sn, равен S1 + S2 + ... + Sn;
  • Размер компоновки объединения U с компоновками-членами M1, M2, ... Mn, размеры которых равны соответственно S1, S2, ... Sn, равен max(S1, S2, ... Sn).

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

  • Естественное выравнивание компоновки-заполнителя равно 1;
  • Естественное выравнивание компоновки значения размером N равно N;
  • Естественное выравнивание компоновки последовательности с компоновкой элемента E равно выравниванию E;
  • Естественное выравнивание групповой компоновки с компоновками-членами M1, M2, ... Mn, выравнивания которых равны соответственно A1, A2, ... An, равно max(A1, A2 ... An).
При необходимости выравнивание компоновки можно переопределить (см. withByteAlignment(long)). Это может быть полезно для описания компоновок с менее строгими или более строгими требованиями к выравниванию.

Пути компоновки

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

Пути компоновки можно использовать, чтобы:

  • получать смещения произвольно вложенных компоновок;
  • получать дескриптор переменной, с помощью которого можно получить доступ к значению, соответствующему выбранной компоновке;
  • выбирать произвольно вложенную компоновку.

Например, для созданной выше компоновки последовательности taggedValues можно получить смещение в байтах компоновки-члена с именем value в первом элементе последовательности следующим образом:

long valueOffset = TAGGED_VALUES.byteOffset(PathElement.sequenceElement(0),
                                          PathElement.groupElement("value")); // yields 4
Аналогичным образом можно выбрать компоновку-член с именем value:
MemoryLayout value = TAGGED_VALUES.select(PathElement.sequenceElement(),
                                         PathElement.groupElement("value"));

Открытые элементы пути

Некоторые элементы пути компоновки, называемые открытыми элементами пути, могут одновременно выбирать несколько компоновок. Например, открытые элементы пути MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(long, long) выбирают неопределённый элемент в компоновке последовательности. Дескриптор переменной, полученный из пути компоновки, содержащего один или несколько открытых элементов пути, имеет дополнительные координаты типа long, которые клиенты могут использовать для привязки открытых элементов пути:
VarHandle valueHandle = TAGGED_VALUES.varHandle(PathElement.sequenceElement(),
                                               PathElement.groupElement("value"));
MemorySegment taggedValues = ...
// reads the "value" field of the third struct in the array (taggedValues[2].value)
int val = (int) valueHandle.get(taggedValues,
        0L,  // base offset
        2L); // sequence index

Открытые элементы пути также влияют на создание дескрипторов метода для вычисления смещения. Каждый открытый элемент пути становится дополнительным параметром типа long в полученном дескрипторе метода. Этот параметр можно использовать для указания индекса элемента последовательности, смещение которого требуется вычислить:

MethodHandle offsetHandle = TAGGED_VALUES.byteOffsetHandle(PathElement.sequenceElement(),
                                                          PathElement.groupElement("kind"));
long offset1 = (long) offsetHandle.invokeExact(0L, 1L); // 0 + (1 * 8) = 8
long offset2 = (long) offsetHandle.invokeExact(0L, 2L); // 0 + (2 * 8) = 16

Элементы пути разыменования

Особый вид элемента пути, называемый элементом пути разыменования, позволяет дескрипторам переменных, полученным из компоновок памяти, следовать по указателям. Рассмотрим следующую компоновку:
StructLayout RECTANGLE = MemoryLayout.structLayout(
        ValueLayout.ADDRESS.withTargetLayout(
                MemoryLayout.sequenceLayout(4,
                        MemoryLayout.structLayout(
                                ValueLayout.JAVA_INT.withName("x"),
                                ValueLayout.JAVA_INT.withName("y")
                        ).withName("point")
                 )
         ).withName("points")
);
Эта компоновка структуры описывает прямоугольник. Она содержит одно поле — points, компоновку адреса, целевой компоновкой которой является компоновка последовательности из четырёх компоновок структур. Каждая компоновка структуры описывает двумерную точку и задаётся парой координат ValueLayout.JAVA_INT с именами x и y соответственно.

С помощью элементов пути разыменования можно получить дескриптор переменной, который обращается к координате y одной из точек прямоугольника, следующим образом:

VarHandle rectPointYs = RECTANGLE.varHandle(
        PathElement.groupElement("points"),
        PathElement.dereferenceElement(),
        PathElement.sequenceElement(),
        PathElement.groupElement("y")
);

MemorySegment rect = ...
// dereferences the third point struct in the "points" array, and reads its "y" coordinate (rect.points[2]->y)
int rect_y_2 = (int) rectPointYs.get(rect,
    0L,  // base offset
    2L); // sequence index

Корректность путей компоновки

Путь компоновки применяется к компоновке C_0, также называемой исходной компоновкой. Каждый элемент пути в пути компоновки можно рассматривать как функцию, которая обновляет текущую компоновку C_i-1 до некоторой другой компоновки C_i. Иными словами, для каждого элемента пути E1, E2, ... En в пути компоновки P вычисляется C_i = f_i(C_i-1), где f_i — функция выбора, связанная с рассматриваемым элементом пути, обозначаемая как E_i. Итоговая компоновка C_i также называется выбранной компоновкой.

Путь компоновки P считается корректным для исходной компоновки C_0, если все его элементы пути E1, E2, ... En корректны для соответствующих входных компоновок C_0, C_1, ... C_n-1. Элемент пути E считается корректным для компоновки L, если выполняется любое из следующих условий:

  • L является компоновкой последовательности, а E — элементом пути последовательности (одним из MemoryLayout.PathElement.sequenceElement(long), MemoryLayout.PathElement.sequenceElement(long, long) или MemoryLayout.PathElement.sequenceElement()). Кроме того, если E содержит один или несколько индексов последовательности, эти индексы должны быть совместимы с количеством элементов компоновки последовательности;
  • L является групповой компоновкой, а E — элементом пути группы (одним из MemoryLayout.PathElement.groupElement(String) или MemoryLayout.PathElement.groupElement(long)). Кроме того, элемент пути группы должен ссылаться на допустимую компоновку-член в L — по имени или индексу;
  • L является компоновкой адреса, а E — элементом пути разыменования. Кроме того, L должна определять некоторую целевую компоновку.
Любая попытка передать путь компоновки P, который некорректен для исходной компоновки C_0, приведёт к выбросу исключения IllegalArgumentException.

Ограничения режимов доступа

Дескриптор переменной, возвращаемый методом varHandle(PathElement...) или ValueLayout.varHandle(), обладает определёнными характеристиками доступа, которые определяются выбранной компоновкой L:
  • Тип-переносчик T, определяемый по L.carrier()
  • Ограничение выравнивания A, определяемое по L.byteAlignment()
  • Размер доступа S, определяемый по L.byteSize()
В зависимости от перечисленных характеристик возвращённый дескриптор переменной может иметь определённые ограничения режимов доступа. Дескриптор переменной называется выровненным, если его ограничение выравнивания A совместимо с размером доступа S, то есть если A >= S. Гарантируется, что выровненный дескриптор переменной поддерживает следующие режимы доступа:
  • режимы доступа для чтения и записи для всех T. Режимы доступа get и set для long, double и MemorySegment поддерживаются, но не гарантируют атомарность, как описано в разделе 17.7 спецификации Язык программирования Java.
  • режимы атомарного обновления для int, long, float, double и MemorySegment. (В будущих основных выпусках платформы JDK для некоторых режимов доступа, которые сейчас не поддерживаются, могут быть добавлены дополнительные типы.)
  • режимы числового атомарного обновления для int, long и MemorySegment. (В будущих основных выпусках платформы JDK для некоторых режимов доступа, которые сейчас не поддерживаются, могут быть добавлены дополнительные числовые типы.)
  • режимы побитового атомарного обновления для int, long и MemorySegment. (В будущих основных выпусках платформы JDK для некоторых режимов доступа, которые сейчас не поддерживаются, могут быть добавлены дополнительные числовые типы.)
Если T — это float, double или MemorySegment, то режимы атомарного обновления сравнивают значения, используя их побитовое представление (см. Float.floatToRawIntBits(float), Double.doubleToRawLongBits(double) и MemorySegment.address() соответственно).

В противном случае дескриптор переменной называется невыровненным, если его ограничение выравнивания A несовместимо с размером доступа S, то есть если A < S. Невыровненный дескриптор переменной поддерживает только режимы доступа get и set. При использовании любых других режимов доступа будет выброшено исключение UnsupportedOperationException. Кроме того, хотя режимы доступа get и set поддерживаются, они могут привести к разрыву слова.

Работа с массивами переменной длины

Мы рассмотрели, как компоновки последовательностей используются для описания содержимого массива, размер которого известен статически. Однако бывают случаи, когда размер массива известен только динамически. Такие массивы называются массивами переменной длины. Существует два распространённых вида массивов переменной длины:
  • массив переменной длины верхнего уровня, размер которого зависит от значения некоторой не связанной с ним переменной или параметра;
  • массив переменной длины, вложенный в структуру, размер которого зависит от значения другого поля во внешней структуре.
Хотя массивы переменной длины нельзя напрямую моделировать с помощью компоновок последовательностей, клиенты всё же могут получать структурированный доступ к элементам таких массивов с помощью дескрипторов переменных, как показано в следующих разделах.

Массивы переменной длины верхнего уровня

Рассмотрим следующее объявление структуры на C:
typedef struct {
    int x;
    int y;
} Point;
В приведённом выше коде точка моделируется двумя координатами (соответственно x и y). Теперь рассмотрим следующий фрагмент кода на C:
int size = ...
Point *points = (Point*)malloc(sizeof(Point) * size);
for (int i = 0 ; i < size ; i++) {
   ... points[i].x ...
}
Здесь мы выделяем массив точек (points). Важно, что размер массива динамически определяется значением переменной size. Внутри цикла выполняется обращение к координате x всех точек массива.

Чтобы смоделировать этот код на Java, сначала определим компоновку структуры Point следующим образом:

StructLayout POINT = MemoryLayout.structLayout(
            ValueLayout.JAVA_INT.withName("x"),
            ValueLayout.JAVA_INT.withName("y")
);
Поскольку нам нужно создать массив точек и обращаться к его элементам, может показаться заманчивым создать компоновку последовательности, моделирующую массив переменной длины, а затем получить из неё необходимые дескрипторы переменных для доступа. Однако этот подход проблематичен, так как размер массива переменной длины неизвестен. Вместо этого дескриптор переменной, обеспечивающий структурированный доступ к элементам массива переменной длины, можно получить непосредственно из компоновки, описывающей элементы массива (например, из компоновки точки), как показано ниже:
VarHandle POINT_ARR_X = POINT.arrayElementVarHandle(PathElement.groupElement("x"));

int size = ...
MemorySegment points = ...
for (int i = 0 ; i < size ; i++) {
    ... POINT_ARR_X.get(points, 0L, (long)i) ...
}
Здесь к координате x последующих точек массива обращаются с помощью дескриптора переменной POINT_ARR_X, полученного методом arrayElementVarHandle(PathElement...). У этого дескриптора переменной есть две координаты long: первая — базовое смещение (равное 0L), а вторая — логический индекс, который позволяет перемещаться по элементам массива точек.

Координата базового смещения позволяет клиентам задавать сложные операции доступа, добавляя дополнительные вычисления смещения в дескриптор переменной (пример будет приведён ниже). Если базовое смещение постоянно (как в предыдущем примере), клиенты при желании могут исключить параметр базового смещения и упростить выражение доступа. Это можно сделать с помощью адаптера дескриптора переменной MethodHandles.insertCoordinates(VarHandle, int, Object...).

Вложенные массивы переменной длины

Рассмотрим следующее объявление структуры на C:
typedef struct {
    int size;
    Point points[];
} Polygon;
В приведённом выше коде многоугольник моделируется размером (количеством его рёбер) и массивом точек (по одной для каждой вершины многоугольника). Количество вершин зависит от количества рёбер. Поэтому размер массива points в объявлении на C оставлен неуказанным с использованием гибкого элемента массива (функция, стандартизированная в C99).

И здесь клиенты могут выполнять структурированный доступ к элементам вложенного массива переменной длины с помощью метода arrayElementVarHandle(PathElement...), как показано ниже:

StructLayout POLYGON = MemoryLayout.structLayout(
            ValueLayout.JAVA_INT.withName("size"),
            MemoryLayout.sequenceLayout(0, POINT).withName("points")
);

VarHandle POLYGON_SIZE = POLYGON.varHandle(0, PathElement.groupElement("size"));
long POINTS_OFFSET = POLYGON.byteOffset(PathElement.groupElement("points"));
Компоновка POLYGON содержит компоновку последовательности нулевого размера. Компоновка элемента этой последовательности — показанная ранее компоновка POINT. Компоновка многоугольника используется для получения дескриптора переменной, обеспечивающего доступ к размеру многоугольника, а также смещения (POINTS_OFFSET) до начала массива переменной длины points.

Затем к координатам x всех точек многоугольника можно обращаться следующим образом:

MemorySegment polygon = ...
int size = POLYGON_SIZE.get(polygon, 0L);
for (int i = 0 ; i < size ; i++) {
    ... POINT_ARR_X.get(polygon, POINTS_OFFSET, (long)i) ...
}
Сначала мы получаем размер многоугольника с помощью дескриптора переменной POLYGON_SIZE. Затем в цикле считываем координаты x всех точек многоугольника. Для этого координате смещения дескриптора переменной POINT_ARR_X передаётся пользовательское смещение (а именно POINTS_OFFSET). Как и прежде, переменная цикла i передаётся в качестве индекса дескриптора переменной POINT_ARR_X, чтобы перемещаться по всем элементам массива переменной длины.
Требования к реализации:
Реализации этого интерфейса неизменяемы, потокобезопасны и основаны на значениях.
Граф запечатанной иерархии классов:
Sealed class hierarchy graph for MemoryLayoutSealed class hierarchy graph for MemoryLayout
Начиная с версии:
22

Краткое описание вложенных классов

Модификатор и тип Интерфейс Описание
static interface  MemoryLayout.PathElement
Элемент пути компоновки.

Краткое описание методов

Модификатор и тип Метод Описание
VarHandle arrayElementVarHandle(MemoryLayout.PathElement... elements)
Создаёт дескриптор переменной, который обращается к соседним элементам сегмента памяти со смещениями, выбранными заданным путём компоновки, где компоновки доступных элементов совпадают с этой компоновкой, а исходная компоновка в пути — эта компоновка.
long byteAlignment()
Возвращает ограничение выравнивания, связанное с этой компоновкой и выраженное в байтах.
long byteOffset(MemoryLayout.PathElement... elements)
Вычисляет смещение в байтах компоновки, выбранной заданным путём компоновки, где исходная компоновка в пути — эта компоновка.
MethodHandle byteOffsetHandle(MemoryLayout.PathElement... elements)
Создаёт дескриптор метода, вычисляющий смещение в байтах компоновки, выбранной заданным путём компоновки, где исходная компоновка в пути — эта компоновка.
long byteSize()
Возвращает размер компоновки в байтах.
boolean equals(Object other)
Сравнивает указанный объект с этой компоновкой на равенство.
int hashCode()
Возвращает хеш-код этой компоновки.
Optional<String> name()
Возвращает имя (если оно есть), связанное с этой компоновкой.
static PaddingLayout paddingLayout(long byteSize)
Создаёт компоновку-заполнитель заданного размера в байтах.
long scale(long offset, long index)
Возвращает offset + (byteSize() * index).
MethodHandle scaleHandle()
Возвращает дескриптор метода, который можно использовать для вызова scale(long, long) для этой компоновки.
MemoryLayout select(MemoryLayout.PathElement... elements)
Возвращает компоновку, выбранную по указанному пути, где исходная компоновка в пути — эта компоновка.
static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout)
Создаёт компоновку последовательности с заданной компоновкой элемента и количеством элементов.
MethodHandle sliceHandle(MemoryLayout.PathElement... elements)
Создаёт дескриптор метода, который для заданного сегмента памяти возвращает срез, соответствующий компоновке, выбранной заданным путём компоновки, где исходная компоновка в пути — эта компоновка.
static StructLayout structLayout(MemoryLayout... elements)
Создаёт компоновку структуры с заданными компоновками-членами.
String toString()
Возвращает строковое представление этой компоновки.
static UnionLayout unionLayout(MemoryLayout... elements)
Создаёт компоновку объединения с заданными компоновками-членами.
VarHandle varHandle(MemoryLayout.PathElement... elements)
Создаёт дескриптор переменной, который обращается к сегменту памяти со смещением, выбранным заданным путём компоновки, где исходная компоновка в пути — эта компоновка.
MemoryLayout withByteAlignment(long byteAlignment)
Возвращает компоновку памяти с теми же характеристиками, что и у этой компоновки, но с заданным ограничением выравнивания (в байтах).
MemoryLayout withName(String name)
Возвращает компоновку памяти с теми же характеристиками, что и у этой компоновки, но с заданным именем.
MemoryLayout withoutName()
Возвращает компоновку памяти с теми же характеристиками, что и у этой компоновки, но без имени.

Подробное описание методов

byteSize

long byteSize()
Возвращает размер макета в байтах.
Возвращает:
размер макета в байтах

name

Optional<String> name()
Возвращает имя (если оно есть), связанное с этим макетом.
Возвращает:
имя (если оно есть), связанное с этим макетом
См. также:
  • withName(String)

withName

MemoryLayout withName(String name)
Возвращает макет памяти с теми же характеристиками, что и у этого макета, но с заданным именем.
Параметры:
name — имя макета
Возвращает:
макет памяти с теми же характеристиками, что и у этого макета, но с заданным именем
См. также:
  • name()

withoutName

MemoryLayout withoutName()
Возвращает макет памяти с теми же характеристиками, что и у этого макета, но без имени.
Примечание к API:
Это может быть полезно для сравнения двух макетов, имеющих разные имена, но в остальном равных.
Возвращает:
макет памяти с теми же характеристиками, что и у этого макета, но без имени
См. также:
  • name()

byteAlignment

long byteAlignment()
Возвращает ограничение выравнивания, связанное с этим макетом и выраженное в байтах.

Выравнивание макета задаёт степень двойки A, которая является выравниванием макета по границе байта, где A — число байтов, по которому должен быть выровнен любой указатель, корректно указывающий на этот макет. Таким образом:

  • A=1 означает отсутствие выравнивания (в обычном смысле), что часто встречается в пакетах.
  • A=8 означает выравнивание по слову (в LP64), A=4 — выравнивание по int, A=2 — выравнивание по short и т. д.
  • A=64 — наиболее строгое выравнивание, требуемое ABI x86/SV (для данных AVX-512).
Если для этого макета не задано явное ограничение выравнивания (см. withByteAlignment(long)), этот метод возвращает ограничение естественного выравнивания (в байтах), связанное с этим макетом.
Возвращает:
ограничение выравнивания, связанное с этим макетом и выраженное в байтах

withByteAlignment

MemoryLayout withByteAlignment(long byteAlignment)
Возвращает макет памяти с теми же характеристиками, что и у этого макета, но с заданным ограничением выравнивания (в байтах).
Параметры:
byteAlignment — ограничение выравнивания макета, выраженное в байтах
Возвращает:
макет памяти с теми же характеристиками, что и у этого макета, но с заданным ограничением выравнивания (в байтах)
Вызывает исключение:
IllegalArgumentException — если byteAlignment не является степенью двойки

scale

long scale(long offset, long index)
Возвращает offset + (byteSize() * index).
Параметры:
offset — базовое смещение
index — индекс, масштабируемый на размер этого макета в байтах
Возвращает:
offset + (byteSize() * index)
Вызывает исключение:
IllegalArgumentException — если offset или index отрицательно
ArithmeticException — если происходит переполнение при сложении или умножении

scaleHandle

MethodHandle scaleHandle()
Возвращает дескриптор метода, который можно использовать для вызова scale(long, long) для этого макета.
Возвращает:
дескриптор метода, который можно использовать для вызова scale(long, long) для этого макета

byteOffset

long byteOffset(MemoryLayout.PathElement... elements)
Вычисляет смещение в байтах макета, выбранного заданным путём макета, начальным макетом которого является этот макет.
Параметры:
elements — элементы пути макета
Возвращает:
Смещение в байтах макета, выбранного путём макета в elements
Вызывает исключение:
IllegalArgumentException — если путь макета имеет некорректную форму для этого макета
IllegalArgumentException — если путь макета содержит один или несколько открытых элементов пути
IllegalArgumentException — если путь макета содержит один или несколько элементов пути разыменования

byteOffsetHandle

MethodHandle byteOffsetHandle(MemoryLayout.PathElement... elements)
Создаёт дескриптор метода, который вычисляет смещение в байтах макета, выбранного заданным путём макета, начальным макетом которого является этот макет.

Возвращённый дескриптор метода имеет следующие характеристики:

  • его тип возвращаемого значения — long;
  • у него есть один начальный параметр типа long, представляющий базовое смещение;
  • у него есть ноль или более конечных параметров типа long — по одному для каждого открытого элемента пути в предоставленном пути макета. Порядок этих параметров соответствует порядку открытых элементов пути в предоставленном пути макета.

Итоговое смещение, возвращаемое дескриптором метода, вычисляется следующим образом:

offset = b + c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
где b представляет базовое смещение, переданное в качестве динамического аргумента long, x_1, x_2, ... x_n представляют индексы в последовательностях, переданные в качестве динамических аргументов long, тогда как s_1, s_2, ... s_n — другие статические константы шага, полученные из размера макета элемента последовательности, а c_1, c_2, ... c_m — другие статические константы смещения (например, смещения полей), полученные из пути макета.

Для любого динамического аргумента x_i должно выполняться условие 0 <= x_i < size_i, где size_i — размер открытого элемента пути, связанного с x_i. В противном случае возвращённый дескриптор метода выбрасывает исключение IndexOutOfBoundsException. Кроме того, значение b должно быть таким, чтобы вычисление offset не приводило к переполнению; иначе возвращённый дескриптор метода выбрасывает исключение ArithmeticException.

Примечание к API:
Возвращённый дескриптор метода можно использовать для вычисления смещения макета аналогично byteOffset(PathElement...), но более гибким образом, поскольку некоторые индексы можно задать при вызове дескриптора метода.
Параметры:
elements — элементы пути макета
Возвращает:
дескриптор метода, вычисляющий смещение в байтах макета, выбранного заданным путём макета
Вызывает исключение:
IllegalArgumentException — если путь макета имеет некорректную форму для этого макета
IllegalArgumentException — если путь макета содержит один или несколько элементов пути разыменования

varHandle

VarHandle varHandle(MemoryLayout.PathElement... elements)
Создаёт дескриптор переменной, обращающийся к сегменту памяти по смещению, выбранному заданным путём макета, начальным макетом которого является этот макет.

Возвращённый дескриптор переменной имеет следующие характеристики:

  • его тип определяется типом-переносчиком выбранного макета значения;
  • у него есть начальный параметр типа MemorySegment, представляющий сегмент, к которому выполняется обращение
  • за ним следует параметр long, соответствующий базовому смещению, обозначенному как B;
  • у него есть ноль или более конечных координат доступа типа long — по одной для каждого открытого элемента пути в предоставленном пути макета, обозначенных соответственно как I1, I2, ... In. Порядок этих координат доступа соответствует порядку открытых элементов пути в предоставленном пути макета.

Если предоставленный путь макета P не содержит элементов разыменования, смещение O операции доступа вычисляется следующим образом:

O = this.byteOffsetHandle(P).invokeExact(B, I1, I2, ... In);

Доступ к сегменту памяти с помощью дескриптора переменной, возвращённого этим методом, сопровождается следующими проверками:

  • Физический адрес сегмента памяти, к которому выполняется обращение, должен быть выровнен в соответствии с ограничением выравнивания корневого макета (этого макета); в противном случае выбрасывается IllegalArgumentException. Обратите внимание, что ограничение выравнивания корневого макета может быть строже (но не слабее) ограничения выравнивания выбранного макета значения.
  • Операция доступа должна находиться в пространственных границах сегмента памяти, к которому выполняется обращение; в противном случае выбрасывается IndexOutOfBoundsException. Это условие выполняется, когда B + A <= S, где B — базовое смещение (определённое выше), A — размер этого макета, а S — размер сегмента памяти, к которому выполняется обращение. Обратите внимание, что размер этого макета может быть больше размера макета, к которому выполняется обращение (например, при обращении к члену структуры).
  • Если предоставленный путь макета содержит открытый элемент пути с размером S, значение соответствующей конечной координаты long — I — должно быть равно 0 <= I < S; в противном случае выбрасывается IndexOutOfBoundsException.
  • Сегмент памяти, к которому выполняется обращение, должен быть accessible для потока, выполняющего операцию доступа; в противном случае выбрасывается WrongThreadException.
  • При операциях записи сегмент памяти, к которому выполняется обращение, не должен быть read only; в противном случае выбрасывается IllegalArgumentException.
  • Область действия, связанная с сегментом, к которому выполняется обращение, должна быть активна; в противном случае выбрасывается IllegalStateException.

Если выбранный макет является макетом адреса, вызов VarHandle.get(Object...) для возвращённого дескриптора переменной вернёт новый сегмент памяти. Сегмент будет связан с глобальной областью действия. Кроме того, размер сегмента зависит от того, задан ли у макета адреса целевой макет. В частности:

  • Если у макета адреса задан целевой макет T, размер возвращённого сегмента равен T.byteSize();
  • В противном случае у макета адреса нет целевого макета, и размер возвращённого сегмента равен нулю.
Кроме того, если выбранный макет является макетом адреса, вызов VarHandle.set(Object...) может выбросить IllegalArgumentException, если сегмент памяти, представляющий записываемый адрес, не является нативным сегментом памяти.

Если предоставленный путь макета имеет размер m и содержит элемент пути разыменования в позиции k (где k <= m), то выводятся два пути макета P и Q, где P содержит все элементы пути от 0 до k - 1, а Q содержит все элементы пути от k + 1 до m (Q может быть пустым путём макета, если k == m). Затем возвращённый дескриптор переменной вычисляется следующим образом:

VarHandle baseHandle = this.varHandle(P);
MemoryLayout target = ((AddressLayout)this.select(P)).targetLayout().get();
VarHandle targetHandle = target.varHandle(Q);
targetHandle = MethodHandles.insertCoordinates(targetHandle, 1, 0L); // always access nested targets at offset 0
targetHandle = MethodHandles.collectCoordinates(targetHandle, 0,
        baseHandle.toMethodHandle(VarHandle.AccessMode.GET));
(Приведённое выше можно без труда обобщить на случаи, когда предоставленный путь макета содержит более одного элемента пути разыменования.)

В качестве примера рассмотрим макет памяти, выраженный экземпляром GroupLayout, созданным следующим образом:

    GroupLayout grp = java.lang.foreign.MemoryLayout.structLayout(
            MemoryLayout.paddingLayout(4),
            ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value")
    );
Чтобы получить доступ к макету члена с именем value, можно создать дескриптор переменной следующим образом:
    VarHandle handle = grp.varHandle(PathElement.groupElement("value")); //(MemorySegment, long) -> int
Примечание к API:
Полученный дескриптор переменной имеет определённые ограничения режимов доступа, общие для всех дескрипторов переменных, производных от макетов памяти.
Параметры:
elements — элементы пути макета
Возвращает:
дескриптор переменной, обращающийся к сегменту памяти по смещению, выбранному заданным путём макета
Вызывает исключение:
IllegalArgumentException — если путь макета имеет некорректную форму для этого макета
IllegalArgumentException — если макет, выбранный заданным путём, не является макетом значения

arrayElementVarHandle

VarHandle arrayElementVarHandle(MemoryLayout.PathElement... elements)
Создаёт дескриптор переменной, обращающийся к соседним элементам сегмента памяти по смещениям, выбранным заданным путём макета; доступ к этим элементам осуществляется с использованием этого макета, а начальным макетом в пути является этот макет.

Возвращённый дескриптор переменной имеет следующие характеристики:

  • его тип определяется типом-переносчиком выбранного макета значения;
  • у него есть начальный параметр типа MemorySegment, представляющий сегмент, к которому выполняется обращение
  • за ним следует параметр long, соответствующий базовому смещению, обозначенному как B;
  • за ним следует параметр long, соответствующий индексу массива, обозначенному как I0. Индекс массива используется для масштабирования смещения доступа на размер этого макета;
  • у него есть ноль или более конечных координат доступа типа long — по одной для каждого открытого элемента пути в предоставленном пути макета, обозначенных соответственно как I1, I2, ... In. Порядок этих координат доступа соответствует порядку открытых элементов пути в предоставленном пути макета.

Если предоставленный путь макета P не содержит элементов разыменования, смещение O операции доступа вычисляется следующим образом:

O = this.byteOffsetHandle(P).invokeExact(this.scale(B, I0), I1, I2, ... In);

Формально дескриптор метода, возвращённый этим методом, получается из varHandle(PathElement...) следующим образом:

MethodHandles.collectCoordinates(varHandle(elements), 1, scaleHandle())

Доступ к сегменту памяти с помощью дескриптора переменной, возвращённого этим методом, сопровождается следующими проверками:

  • Физический адрес сегмента памяти, к которому выполняется обращение, должен быть выровнен в соответствии с ограничением выравнивания корневого макета (этого макета); в противном случае выбрасывается IllegalArgumentException. Обратите внимание, что ограничение выравнивания корневого макета может быть строже (но не слабее) ограничения выравнивания выбранного макета значения.
  • Операция доступа должна находиться в пространственных границах сегмента памяти, к которому выполняется обращение; в противном случае выбрасывается IndexOutOfBoundsException. Это условие выполняется, когда B + A <= S, где B — базовое смещение (определённое выше), A — размер этого макета, а S — размер сегмента памяти, к которому выполняется обращение. Обратите внимание, что размер этого макета может быть больше размера макета, к которому выполняется обращение (например, при обращении к члену структуры).
  • Если предоставленный путь макета содержит открытый элемент пути с размером S, значение соответствующей конечной координаты long — I — должно быть равно 0 <= I < S; в противном случае выбрасывается IndexOutOfBoundsException.
  • Сегмент памяти, к которому выполняется обращение, должен быть accessible для потока, выполняющего операцию доступа; в противном случае выбрасывается WrongThreadException.
  • При операциях записи сегмент памяти, к которому выполняется обращение, не должен быть read only; в противном случае выбрасывается IllegalArgumentException.
  • Область действия, связанная с сегментом, к которому выполняется обращение, должна быть активна; в противном случае выбрасывается IllegalStateException.
Примечание к API:
Поскольку начальная координата индекса I0 не ограничена никаким макетом последовательности, она может принимать любое неотрицательное значение при условии, что вычисление итогового смещения не приводит к переполнению и вычисленное смещение не выходит за пространственные границы сегмента памяти, к которому выполняется обращение. Поэтому дескрипторы переменных, возвращаемые этим методом, могут быть особенно полезны при работе с массивами переменной длины.
Параметры:
elements — элементы пути макета
Возвращает:
дескриптор переменной, обращающийся к соседним элементам сегмента памяти по смещениям, выбранным заданным путём макета
Вызывает исключение:
IllegalArgumentException — если путь макета имеет некорректную форму для этого макета
IllegalArgumentException — если макет, выбранный заданным путём, не является макетом значения

sliceHandle

MethodHandle sliceHandle(MemoryLayout.PathElement... elements)
Создаёт дескриптор метода, который получает сегмент памяти и возвращает срез, соответствующий макету, выбранному заданным путём макета, начальным макетом которого является этот макет.

Возвращённый дескриптор метода имеет следующие характеристики:

  • его тип возвращаемого значения — MemorySegment;
  • у него есть начальный параметр типа MemorySegment, соответствующий сегменту памяти, который необходимо нарезать
  • за ним следует параметр long, соответствующий базовому смещению
  • у него есть ноль или более конечных параметров типа long — по одному для каждого открытого элемента пути в предоставленном пути макета. Порядок этих параметров соответствует порядку открытых элементов пути в предоставленном пути макета.

Смещение O возвращённого сегмента вычисляется так же, как при вызове дескриптора смещения в байтах, созданного с использованием заданных элементов пути.

Вычисление среза сегмента памяти с помощью дескриптора метода, возвращённого этим методом, сопровождается следующими проверками:

  • Физический адрес сегмента памяти, к которому выполняется обращение, должен быть выровнен в соответствии с ограничением выравнивания корневого макета (этого макета); в противном случае будет выброшено IllegalArgumentException. Обратите внимание, что ограничение выравнивания корневого макета может быть строже (но не слабее) ограничения выравнивания выбранного макета.
  • Операция создания среза должна находиться в пространственных границах сегмента памяти, к которому выполняется обращение; в противном случае выбрасывается IndexOutOfBoundsException. Это условие выполняется, когда B + A <= S, где B — базовое смещение (определённое выше), A — размер этого макета, а S — размер сегмента памяти, к которому выполняется обращение. Обратите внимание, что размер этого макета может быть больше размера макета, к которому выполняется обращение (например, при обращении к члену структуры).
  • Если предоставленный путь макета содержит открытый элемент пути с размером S, значение соответствующей конечной координаты long — I — должно быть равно 0 <= I < S; в противном случае выбрасывается IndexOutOfBoundsException.
Примечание к API:
Возвращённый дескриптор метода можно использовать для получения среза сегмента памяти аналогично MemorySegment.asSlice(long, long), но более гибким образом, поскольку некоторые индексы можно задать при вызове дескриптора метода.
Параметры:
elements — элементы пути макета
Возвращает:
дескриптор метода, используемый для создания среза сегмента памяти по смещению, выбранному заданным путём макета
Вызывает исключение:
IllegalArgumentException — если путь макета имеет некорректную форму для этого макета
IllegalArgumentException — если путь макета содержит один или несколько элементов пути разыменования

select

MemoryLayout select(MemoryLayout.PathElement... elements)
Возвращает макет, выбранный по предоставленному пути, начальным макетом которого является этот макет.
Параметры:
elements — элементы пути макета
Возвращает:
макет, выбранный путём макета в elements
Вызывает исключение:
IllegalArgumentException — если путь макета имеет некорректную форму для этого макета
IllegalArgumentException — если путь макета содержит один или несколько элементов пути разыменования
IllegalArgumentException — если путь макета содержит один или несколько элементов пути, выбирающих один или несколько индексов элементов последовательности, например MemoryLayout.PathElement.sequenceElement(long) и MemoryLayout.PathElement.sequenceElement(long, long))

equals

boolean equals(Object other)
Сравнивает указанный объект с этим макетом на равенство. Возвращает true тогда и только тогда, когда указанный объект также является макетом и равен этому макету. Два макета считаются равными, если они имеют один и тот же вид, одинаковые размер, имя и ограничение выравнивания. Кроме того, в зависимости от вида макета должны выполняться дополнительные условия:
  • два макета значения считаются равными, если у них одинаковые порядок байтов и тип-переносчик. Кроме того, два макета адреса считаются равными, если у них также одинаковый целевой макет;
  • два макета последовательности считаются равными, если у них одинаковое количество элементов (см. SequenceLayout.elementCount()) и равны их макеты элементов (см. SequenceLayout.elementLayout());
  • два групповых макета считаются равными, если они имеют один и тот же тип (см. StructLayout, UnionLayout) и равны их макеты членов (см. GroupLayout.memberLayouts()).
Переопределяет:
equals в классе Object
Параметры:
other — объект, который сравнивается на равенство с этим макетом
Возвращает:
true, если указанный объект равен этому макету
См. также:
  • Object.hashCode()
  • HashMap

hashCode

int hashCode()
Возвращает хеш-код этого макета.
Переопределяет:
hashCode в классе Object
Возвращает:
хеш-код этого макета
См. также:
  • Object.equals(java.lang.Object)
  • System.identityHashCode(Object)

toString

String toString()
Возвращает строковое представление этого макета.
Переопределяет:
toString в классе Object
Возвращает:
строковое представление этого макета

paddingLayout

static PaddingLayout paddingLayout(long byteSize)
Создаёт макет заполнения с заданным размером в байтах. Ограничение выравнивания возвращённого макета равно 1. Поэтому независимо от его размера, при отсутствии явного ограничения выравнивания макет заполнения не влияет на естественное выравнивание группового макета или макета последовательности, в который он вложен.
Параметры:
byteSize — размер заполнения (в байтах)
Возвращает:
новый макет селектора
Вызывает исключение:
IllegalArgumentException — если byteSize <= 0

sequenceLayout

static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout)
Создаёт макет последовательности с заданным макетом элемента и количеством элементов.
Параметры:
elementCount — количество элементов последовательности
elementLayout — макет элемента последовательности
Возвращает:
новый макет последовательности с заданным макетом элемента и размером
Вызывает исключение:
IllegalArgumentException — если elementCount отрицательно
IllegalArgumentException — если происходит переполнение elementLayout.byteSize() * elementCount
IllegalArgumentException — если elementLayout.byteSize() % elementLayout.byteAlignment() != 0

structLayout

static StructLayout structLayout(MemoryLayout... elements)
Создает структурную компоновку с заданными компоновками элементов.
Примечание к API:
Эта фабрика не выравнивает автоматически компоновки элементов, добавляя дополнительные элементы компоновки заполнения. Поэтому создание следующей структурной компоновки завершится исключением:
structLayout(JAVA_SHORT, JAVA_INT);
Чтобы избежать исключения, клиенты могут добавить дополнительные элементы компоновки заполнения:
structLayout(JAVA_SHORT, MemoryLayout.paddingLayout(2), JAVA_INT);
Или же можно использовать компоновку элемента с менее строгим ограничением на выравнивание. В результате получится упакованная структурная компоновка:
structLayout(JAVA_SHORT, JAVA_INT.withByteAlignment(2));
Параметры:
elements - Компоновки элементов структурной компоновки
Возвращает:
структурную компоновку с заданными компоновками элементов
Вызывает исключение:
IllegalArgumentException - если сумма размеров в байтах компоновок элементов переполняется
IllegalArgumentException - если элемент компоновки в elements находится по смещению (относительно начала структурной компоновки), несовместимому с его ограничением на выравнивание

unionLayout

static UnionLayout unionLayout(MemoryLayout... elements)
Создает объединенную компоновку с заданными компоновками элементов.
Параметры:
elements - Компоновки элементов объединенной компоновки
Возвращает:
объединенную компоновку с заданными компоновками элементов

Сообщить об ошибке или предложить улучшение
Дополнительные справочные материалы по API и документацию для разработчиков см. в документации Java SE, содержащей более подробные описания для разработчиков, обзоры концепций, определения терминов, обходные решения и работающие примеры кода. Другие версии.
Java является товарным знаком или зарегистрированным товарным знаком Oracle и/или ее аффилированных лиц в США и других странах.
Авторские права © 1993, 2025, Oracle и/или ее аффилированные лица, 500 Oracle Parkway, Redwood Shores, CA 94065 USA.
Все права защищены. Использование регулируется условиями лицензии и политикой распространения документации.

© 1993, 2025, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/foreign/MemoryLayout.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API