- 已知的所有子接口:
-
AddressLayout预览
,GroupLayout预览
,PaddingLayout预览
,SequenceLayout预览
,StructLayout预览
,UnionLayout预览
,ValueLayout预览
,ValueLayout.OfBoolean预览
,ValueLayout.OfByte预览
,ValueLayout.OfChar预览
,ValueLayout.OfDouble预览
,ValueLayout.OfFloat预览
,ValueLayout.OfInt预览
,ValueLayout.OfLong预览
,ValueLayout.OfShort预览
MemoryLayout
是 Java 平台的预览 API。
布局层次结构中有两个叶子节点,值布局预览,用于表示给定大小和类型的值(请参阅和填充布局预览,用于表示应忽略内存段的一部分内容,主要出于对齐原因。一些常见的值布局常量,如ValueLayout.JAVA_INT
预览和ValueLayout.JAVA_FLOAT_UNALIGNED
预览在ValueLayout
预览类中定义。一种特殊类型的值布局,即地址布局预览,用于模拟表示内存区域地址的值。
更复杂的布局可以从更简单的布局派生出来:序列布局预览表示零个或多个元素布局的同质重复;组布局预览表示零个或多个成员布局的异构聚合。组布局有两种类型:结构布局预览,其中成员布局依次排列,以及联合布局预览,其中成员布局从相同的起始偏移量开始排列。
布局可以选择性地与一个名称关联。在构建布局路径时,可以引用布局名称。
考虑以下在C中的结构声明:
typedef struct {
char kind;
int value;
} TaggedValues[5];
SequenceLayout taggedValues = 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
预览在64位平台上大小为8字节; - 填充布局的大小始终在构造时明确提供;
- 元素布局为E且元素计数为L的序列布局的大小为E的大小乘以L;
- 成员布局为M1、M2、... Mn,其大小分别为S1、S2、... Sn的结构布局的大小为S1 + S2 + ... + Sn;
- 成员布局为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)
),这对于描述具有更弱或更强对齐约束的布局很有用。
布局路径
布局路径用于明确选择嵌套在其他布局中的布局。布局路径通常表示为一个或多个路径元素预览的序列。(有关布局路径的更正式定义,请参见下文)。布局路径可用于:
- 获取任意嵌套布局的偏移量;
- 获取一个var handle,可用于访问与所选布局对应的值;
- 选择一个任意嵌套布局。
例如,给定上面构建的taggedValues
序列布局,我们可以获取第一个序列元素中名为value
的成员布局的偏移量(以字节为单位),如下所示:
long valueOffset = taggedValues.byteOffset(PathElement.sequenceElement(0),
PathElement.groupElement("value")); // 得到4
value
的成员布局,如下所示:
MemoryLayout value = taggedValues.select(PathElement.sequenceElement(),
PathElement.groupElement("value"));
开放路径元素
一些布局路径元素,称为开放路径元素,可以一次选择多个布局。例如,开放路径元素MemoryLayout.PathElement.sequenceElement()
预览、MemoryLayout.PathElement.sequenceElement(long, long)
预览可以选择序列布局中的未指定元素。从包含一个或多个开放路径元素的布局路径派生的var handle具有额外的long
类型的坐标,客户端可以使用这些坐标绑定路径中的开放元素:
VarHandle valueHandle = taggedValues.varHandle(PathElement.sequenceElement(),
PathElement.groupElement("value"));
MemorySegment valuesSegment = ...
int val = (int) valueHandle.get(valuesSegment, 2); // 读取数组中第三个结构体的"value"字段
开放路径元素还会影响创建计算偏移量的方法句柄。每个开放路径元素在获得的方法句柄中成为一个额外的long
参数。此参数可用于指定要计算偏移量的序列元素的索引:
MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(),
PathElement.groupElement("kind"));
long offset1 = (long) offsetHandle.invokeExact(1L); // 8
long offset2 = (long) offsetHandle.invokeExact(2L); // 16
解引用路径元素
一种特殊类型的路径元素,称为解引用路径元素,允许从内存布局获得的var handle跟随指针。考虑以下布局: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 = ...
int rect_y_4 = (int) rectPointYs.get(rect, 2); // rect.points[2]->y
布局路径的良好形式
布局路径应用于布局C_0
,也称为 初始布局。布局路径中的每个路径元素都可以看作是一个函数,它将当前布局 C_i-1
更新为另一个布局 C_i
。也就是说,对于布局路径中的每个路径元素 E1, E2, ... En
,我们计算 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
都是良好形式的。对于布局 L
,路径元素 E
被认为是对其良好形式的,如果以下任一条件为真:
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
必须定义一些 目标布局预览。
C_0
不良好形式的布局路径 P
将导致 IllegalArgumentException
。
-
Nested Class Summary
-
Method Summary
Modifier and TypeMethodDescriptionlong
返回与此布局关联的对齐约束,以字节表示。default long
byteOffset
(MemoryLayout.PathElementPREVIEW... elements) 计算给定布局路径选择的布局的偏移量(以字节为单位),其中路径中的初始布局是此布局。default MethodHandle
byteOffsetHandle
(MemoryLayout.PathElementPREVIEW... elements) 创建一个方法句柄,计算给定布局路径选择的布局的偏移量(以字节为单位),其中路径中的初始布局是此布局。long
byteSize()
返回布局大小,以字节为单位。boolean
将指定的对象与此布局进行比较以判断是否相等。int
hashCode()
返回此布局的哈希码值。name()
返回与此布局关联的名称(如果有)。static PaddingLayoutPREVIEW
paddingLayout
(long byteSize) 创建具有给定字节大小的填充布局。default MemoryLayoutPREVIEW
select
(MemoryLayout.PathElementPREVIEW... elements) 返回从提供的路径中选择的布局,其中路径中的初始布局是此布局。static SequenceLayoutPREVIEW
sequenceLayout
(long elementCount, MemoryLayoutPREVIEW elementLayout) 使用给定的元素布局和元素计数创建序列布局。static SequenceLayoutPREVIEW
sequenceLayout
(MemoryLayoutPREVIEW elementLayout) 使用给定的元素布局和最大元素计数创建序列布局,以便不会溢出long
。default MethodHandle
sliceHandle
(MemoryLayout.PathElementPREVIEW... elements) static StructLayoutPREVIEW
structLayout
(MemoryLayoutPREVIEW... elements) 使用给定的成员布局创建结构布局。toString()
返回此布局的字符串表示形式。static UnionLayoutPREVIEW
unionLayout
(MemoryLayoutPREVIEW... elements) 使用给定的成员布局创建联合布局。default VarHandle
varHandle
(MemoryLayout.PathElementPREVIEW... elements) 创建一个变量句柄,该句柄访问由给定布局路径选择的偏移量处的内存段,其中路径中的初始布局是此布局。withByteAlignment
(long byteAlignment) 返回具有与此布局相同特征的内存布局,但具有给定的对齐约束(以字节为单位)。返回具有与此布局相同特征的内存布局,但具有给定的名称。返回具有与此布局相同特征的内存布局,但没有名称。
-
Method Details
-
byteSize
long byteSize()返回布局大小,以字节为单位。- 返回:
- 布局大小,以字节为单位
-
name
返回与此布局关联的名称(如果有)。- 返回:
- 与此布局关联的名称(如果有)
- 参见:
-
withName
返回具有与此布局相同特征的内存布局,但具有给定的名称。- 参数:
-
name
- 布局名称。 - 返回:
- 具有与此布局相同特征的内存布局,但具有给定的名称
- 参见:
-
withoutName
MemoryLayoutPREVIEW withoutName()返回具有与此布局相同特征的内存布局,但没有名称。- API 注释:
- 这对于比较具有不同名称但其他方面相等的两个布局可能很有用。
- 返回:
- 具有与此布局相同特征的内存布局,但没有名称
- 参见:
-
byteAlignment
long byteAlignment()返回与此布局关联的对齐约束,以字节表示。布局对齐定义了一个二的幂A
,即布局的字节对齐,其中A
是指向此布局的任何指针必须对齐的字节数。因此:A=1
表示未对齐(通常意义上),在数据包中很常见。A=8
表示字对齐(在 LP64 上),A=4
表示 int 对齐,A=2
表示 short 对齐,依此类推。A=64
是 x86/SV ABI(用于 AVX-512 数据)所需的最严格对齐。
withByteAlignment(long)
),则此方法返回与此布局关联的 自然对齐约束(以字节为单位)。- 返回:
- 与此布局关联的对齐约束,以字节表示
-
withByteAlignment
返回具有与此布局相同特征的内存布局,但具有给定的对齐约束(以字节为单位)。- 参数:
-
byteAlignment
- 布局对齐约束,以字节表示。 - 返回:
- 具有与此布局相同特征的内存布局,但具有给定的对齐约束(以字节为单位)
- 抛出:
-
IllegalArgumentException
- 如果byteAlignment
不是二的幂。
-
byteOffset
计算给定布局路径选择的布局的偏移量(以字节为单位),其中路径中的初始布局是此布局。- 参数:
-
elements
- 布局路径元素。 - 返回:
-
布局路径中
elements
选择的布局的偏移量,以字节为单位。 - 抛出:
-
IllegalArgumentException
- 如果布局路径对于此布局不是 良构的。 -
IllegalArgumentException
- 如果布局路径包含一个或多个 开放路径元素。 -
IllegalArgumentException
- 如果布局路径包含一个或多个 解引用路径元素。
-
byteOffsetHandle
创建一个方法句柄,计算由给定布局路径选择的布局的偏移量(以字节为单位),其中路径中的初始布局是此布局。返回的方法句柄具有以下特征:
- 返回类型为
long
; - 它有零个或多个
long
类型的参数,每个参数对应于提供的布局路径中的每个开放路径元素。这些参数的顺序对应于提供的布局路径中开放路径元素出现的顺序。
方法句柄返回的最终偏移量计算如下:
偏移量 = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
x_1
,x_2
,...x_n
是作为long
参数提供的动态值,而c_1
,c_2
,...c_m
是静态偏移常量,s_0
,s_1
,...s_n
是从布局路径派生的静态步幅常量。- API 注意:
-
返回的方法句柄可用于计算布局偏移量,类似于
byteOffset(PathElement...)
,但更灵活,因为在调用方法句柄时可以指定一些索引。 - 参数:
-
elements
- 布局路径元素。 - 返回:
- 一个方法句柄,计算由给定布局路径选择的布局的偏移量(以字节为单位)。
- 抛出:
-
IllegalArgumentException
- 如果布局路径对于此布局不是良构的。 -
IllegalArgumentException
- 如果布局路径包含一个或多个解引用路径元素。
- 返回类型为
-
varHandle
创建一个变量句柄,访问由给定布局路径选择的偏移量处的内存段,其中路径中的初始布局是此布局。返回的变量句柄具有以下特征:
返回的变量句柄访问的最终地址可以计算如下:
地址 = base(segment) + 偏移量
base(segment)
表示一个函数,返回所访问内存段的物理基地址。对于本机段,此函数只返回本机段的地址预览。对于堆段,此函数更复杂,因为堆段的地址是虚拟化的。偏移量
值可以用以下形式表示:偏移量 = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
x_1
,x_2
,...x_n
是作为long
参数提供的动态值,而c_1
,c_2
,...c_m
是静态偏移常量,s_1
,s_2
,...s_n
是从布局路径派生的静态步幅常量。此外,提供的动态值必须符合从布局路径派生的边界,即
0 <= x_i < b_i
,其中1 <= i <= n
,否则将抛出IndexOutOfBoundsException
。基地址必须根据根布局(此布局)的对齐约束对齐。请注意,这可能比所选值布局的对齐约束更严格(但不会更宽松)。
可以链接多个路径,使用解引用路径元素。解引用路径元素构造一个新的本机内存段,其基地址是通过访问立即在解引用路径元素之前的布局路径元素确定的偏移量的地址值。换句话说,如果布局路径包含一个或多个解引用路径元素,则返回的变量句柄访问的最终地址可以计算如下:
地址_1 = base(segment) + 偏移量_1 地址_2 = base(segment_1) + 偏移量_2 ... 地址_k = base(segment_k-1) + 偏移量_k
k
是布局路径中解引用路径元素的数量,segment
是输入段,segment_1
,...segment_k-1
是通过解引用与给定解引用路径元素关联的地址获得的段(例如,segment_1
是一个基地址为address_1
的本机段),偏移量_1
,偏移量_2
,...偏移量_k
是通过评估给定解引用操作后的路径元素计算的偏移量(这些偏移量是使用上述计算获得的)。在这些更复杂的访问操作中,立即在解引用操作之前执行的所有内存访问(例如,地址为address_1
,address_2
,...,address_k-1
处的访问)都使用VarHandle.AccessMode.GET
访问模式执行。- API 注意:
- 结果变量句柄具有特定的访问模式限制,这些限制适用于所有内存段视图句柄预览。
- 参数:
-
elements
- 布局路径元素。 - 返回:
- 一个变量句柄,访问由给定布局路径选择的偏移量处的内存段。
- 抛出:
-
IllegalArgumentException
- 如果布局路径对于此布局不是良构的。 -
IllegalArgumentException
- 如果由提供的路径选择的布局不是一个值布局预览。 - 参见:
-
sliceHandle
创建一个方法句柄,给定一个内存段,返回与由给定布局路径选择的布局对应的切片预览,其中路径中的初始布局是此布局。返回的方法句柄具有以下特征:
- 返回类型为
MemorySegment
; - 有一个前导参数类型为
MemorySegment
,对应于要切片的内存段; - 有零个或多个
long
类型的参数,每个参数对应于提供的布局路径中的每个开放路径元素。这些参数的顺序对应于提供的布局路径中开放路径元素出现的顺序。
返回段的偏移量计算如下:
long offset = byteOffset(elements); long size = select(elements).byteSize(); MemorySegment slice = segment.asSlice(offset, size);
要切片的段必须根据根布局(此布局)的对齐约束对齐。请注意,这可能比所选值布局的对齐约束更严格(但不会更宽松)。
- API 注意:
-
返回的方法句柄可用于获取内存段切片,类似于
MemorySegment.asSlice(long, long)
预览,但更灵活,因为在调用方法句柄时可以指定一些索引。 - 参数:
-
elements
- 布局路径元素。 - 返回:
- 一个方法句柄,用于在由给定布局路径选择的偏移量处切片内存段。
- 抛出:
-
IllegalArgumentException
- 如果布局路径对于此布局不是良构的。 -
IllegalArgumentException
- 如果布局路径包含一个或多个解引用路径元素。
- 返回类型为
-
select
返回从提供的路径选择的布局,其中路径中的初始布局是此布局。- 参数:
-
elements
- 布局路径元素。 - 返回:
-
由
elements
中的布局路径选择的布局。 - 抛出:
-
IllegalArgumentException
- 如果布局路径对于此布局不符合格式。 -
IllegalArgumentException
- 如果布局路径包含一个或多个解引用路径元素。 -
IllegalArgumentException
- 如果布局路径包含一个或多个选择一个或多个序列元素索引的路径元素,例如MemoryLayout.PathElement.sequenceElement(long)
预览和MemoryLayout.PathElement.sequenceElement(long, long)
预览。
-
equals
将指定的对象与此布局进行比较以确定是否相等。如果指定的对象也是一个布局,并且与此布局相等,则返回true
。如果两个布局是相同类型、具有相同大小、名称和对齐约束,则它们被认为是相等的。此外,根据布局类型的不同,还必须满足其他条件:- 如果两个值布局具有相同的顺序预览和载体预览,则它们被视为相等。此外,如果两个地址布局也具有相同的目标布局预览;
- 如果两个序列布局具有相同的元素计数(参见
SequenceLayout.elementCount()
预览),并且它们的元素布局(参见SequenceLayout.elementLayout()
预览)也相等; - 如果两个组布局是相同类型的(参见
StructLayout
预览,UnionLayout
预览),并且它们的成员布局(参见GroupLayout.memberLayouts()
预览)也相等。
-
hashCode
int hashCode()返回此布局的哈希码值。 -
toString
String toString()返回此布局的字符串表示形式。 -
paddingLayout
使用给定的字节大小创建填充布局。返回的布局的对齐约束为1。因此,在没有显式对齐约束的情况下,填充布局不会影响其嵌套到的组或序列布局的自然对齐。- 参数:
-
byteSize
- 填充大小(以字节表示)。 - 返回:
- 新的选择器布局。
- 抛出:
-
IllegalArgumentException
- 如果byteSize <= 0
。
-
sequenceLayout
使用给定的元素布局和元素计数创建序列布局。- 参数:
-
elementCount
- 序列元素计数。 -
elementLayout
- 序列元素布局。 - 返回:
- 具有给定元素布局和大小的新序列布局。
- 抛出:
-
IllegalArgumentException
- 如果elementCount
为负数。 -
IllegalArgumentException
- 如果elementLayout.byteSize() * elementCount
溢出。 -
IllegalArgumentException
- 如果elementLayout.byteSize() % elementLayout.byteAlignment() != 0
。
-
sequenceLayout
使用给定的元素布局和最大元素计数创建序列布局,以便不会溢出long
。这等效于以下代码:sequenceLayout(Long.MAX_VALUE / elementLayout.byteSize(), elementLayout);
- 参数:
-
elementLayout
- 序列元素布局。 - 返回:
- 具有给定元素布局和最大元素计数的新序列布局。
- 抛出:
-
IllegalArgumentException
- 如果elementLayout.byteSize() % elementLayout.byteAlignment() != 0
。
-
structLayout
使用给定的成员布局创建结构布局。- 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
使用给定的成员布局创建联合布局。- 参数:
-
elements
- 联合布局的成员布局。 - 返回:
- 具有给定成员布局的联合布局。
-
MemoryLayout
。