モジュール java.base
パッケージ java.lang.foreign

インタフェースMemorySegment


public sealed interface MemorySegment
メモリー・セグメントは、メモリーの連続したリージョンへのアクセスを提供します。

メモリー・セグメントには、次の2種類があります:

  • 「ヒープ・セグメント」は、Javaヒープ("on-heap"リージョン)内のメモリー・リージョンによってバックアップされ、アクセスを提供します。
  • 「ネイティブ・セグメント」は、Javaヒープ("off-heap"リージョン)の外部にあるメモリーのリージョンによってバックアップされ、アクセスを提供します。
ヒープ・セグメントは、ofArray(int[])ファクトリ・メソッドのいずれかをコールすることで取得できます。 これらのメソッドは、指定されたJava配列を保持するオン・ヒープ・リージョンによって支えられたメモリー・セグメントを返します。

ネイティブ・セグメントを取得するには、Arena.allocate(long, long)ファクトリ・メソッドの1つをコールします。このメソッドは、指定されたサイズで新しく割り当てられたオフ・ヒープ・リージョンに支えられたメモリー・セグメントを返し、指定された整列制約に合せます。 または、ネイティブ・セグメントを取得するには、ファイルをmappingして新しいオフ・ヒープ・リージョン(一部のシステムでは、この操作をmmapと呼びます)にします。 この方法で取得されるセグメントは、「マップ済」セグメントと呼ばれ、その内容は、基礎となるメモリー・マップ・ファイルとの間で「存続」および「読み込み完了」になります。

どちらの種類のセグメントも、同じメソッド(「アクセス操作」)を使用して読取りおよび書込みが行われます。 メモリー・セグメントに対するアクセス操作は常に行われ、セグメントが取得されたリージョンにのみアクセスできます。

メモリー・セグメントの特性

すべてのメモリー・セグメントには、long値として表されるaddressがあります。 セグメントの住所の性質は、セグメントの種類によって異なります:
  • ヒープ・セグメントのアドレスは物理アドレスではなく、セグメントを支えるメモリー・リージョン内のオフセットです。 リージョンはJavaヒープ内にあるため、ガベージ・コレクションによってリージョンが時間とともに物理メモリーに再配置される可能性がありますが、これは、リージョンによってバックアップされたヒープ・セグメントの安定した「仮想化」アドレスを参照するMemorySegment APIのクライアントには公開されません。 ofArray(int[])ファクトリ・メソッドのいずれかから取得されたヒープ・セグメントのアドレスはゼロです。
  • ネイティブ・セグメント(マップされたセグメントを含む)のアドレスは、セグメントをバックするメモリー・リージョンの物理アドレスを示します。

すべてのメモリー・セグメントにsizeがあります。 ヒープ・セグメントのサイズは、取得元のJava配列から導出されます。 このサイズは、Javaランタイム全体で予測できます。 ネイティブ・セグメントのサイズは、明示的に(Arena.allocate(long, long)のように)に渡されるか、またはMemoryLayout (SegmentAllocator.allocate(MemoryLayout)のように)から導出されます。 通常、メモリー・セグメントのサイズは正の数ですが、zeroの場合もありますが、負の数は発生しません。

メモリー・セグメントのアドレスとサイズは、セグメントに対するアクセス操作が、セグメントをバックするメモリー・リージョンの境界を「外部」下回ることがないようにします。 つまり、メモリー・セグメントには「空間境界」があります。

すべてのメモリー・セグメントは、scopeに関連付けられます。 これにより、メモリー・セグメントをバックするメモリー・リージョンが使用できなくなった場合に、メモリー・セグメントに対するアクセス操作を実行できなくなります。(たとえば、アクセスされたメモリー・セグメントに関連付けられたスコープがaliveではなくなった場合) つまり、メモリー・セグメントには「一時的な境界」があります。

最後に、メモリー・セグメントに対するアクセス操作は、追加のスレッド競合チェックの対象になることがあります。 ヒープ・セグメントには、どのスレッドからでもアクセスできます。 逆に、ネイティブ・セグメントには、その取得に使用されるアリーナの「監禁特性」と互換してのみアクセスできます。

メモリー・セグメントへのアクセス

メモリー・セグメントは、このクラス(e.g. get(ValueLayout.OfInt, long))で提供される様々なアクセス操作を使用して読取りまたは書込みができます。 各アクセス操作には、値のサイズとシェイプ、およびバイト単位のオフセットを指定する「値レイアウト」を使用します。 たとえば、「デフォルトのエンディアン」を使用してセグメントからintを読み取るには、次のコードを使用できます。
MemorySegment segment = ...
int value = segment.get(ValueLayout.JAVA_INT, 0);
読み取る値がbig-endianエンコーディングを使用してメモリーに格納されている場合、アクセス操作は次のように表すことができます:
int value = segment.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0);
メモリー・セグメントに対するアクセス操作は、varハンドルを使用して実装されます。 ValueLayout.varHandle()メソッドを使用すると、指定されたオフセットにあるメモリー・セグメントの指定された値レイアウトによって表される値を取得/設定するために使用できるvarハンドルを取得できます。
VarHandle intAtOffsetHandle = ValueLayout.JAVA_INT.varHandle(); // (MemorySegment, long)
int value = (int) intAtOffsetHandle.get(segment, 10L);          // segment.get(ValueLayout.JAVA_INT, 10L)
または、特定の論理索引でint配列の要素にアクセスするために使用できるvarハンドルを、次のように作成できます。
VarHandle intAtOffsetAndIndexHandle =
        ValueLayout.JAVA_INT.arrayElementVarHandle();             // (MemorySegment, long, long)
int value = (int) intAtOffsetAndIndexHandle.get(segment, 2L, 3L); // segment.get(ValueLayout.JAVA_INT, 2L + (3L * 4L))

クライアントは、アクセス式をよりシンプルにするために、ベース・オフセット・パラメータを削除することもできます。 これは、getAtIndex(OfInt, long)などのアクセス操作の実装に使用できます。

VarHandle intAtIndexHandle =
        MethodHandles.insertCoordinates(intAtOffsetAndIndexHandle, 1, 0L); // (MemorySegment, long)
int value = (int) intAtIndexHandle.get(segment, 3L);                       // segment.getAtIndex(ValueLayout.JAVA_INT, 3L);
より複雑なアクセス式の変数ハンドル(例:構造体フィールド・アクセス、ポインタ間接参照)は、レイアウト・パスを使用して、メモリー・レイアウトから直接作成できます。

メモリー・セグメントのスライス

メモリー・セグメントは「スライス」をサポートしています。 メモリー・セグメントをスライスすると、元のメモリーと同じリージョンにバックアップされた新しいメモリー・セグメントが返されます。 スライスされたセグメントのアドレスは、オフセット(バイトで表されます)を追加して、元のセグメントのアドレスから導出されます。 スライスされたセグメントのサイズは、暗黙的に(元のセグメントのサイズから指定されたオフセットを減算)に導出されるか、明示的に指定されます。 つまり、スライスされたセグメントには、元のセグメントのものよりも「どもり」の空間境界があります:
 Arena arena = ...
 MemorySegment segment = arena.allocate(100);
 MemorySegment slice = segment.asSlice(50, 10);
 slice.get(ValueLayout.JAVA_INT, 20); // Out of bounds!
 arena.close();
 slice.get(ValueLayout.JAVA_INT, 0); // Already closed!
前述のコードでは、長さが100バイトのネイティブ・セグメントが作成され、segmentのオフセット50から始まり、長さが10バイトのスライスが作成されます。 つまり、sliceのアドレスはsegment.address() + 50で、サイズは10です。 その結果、sliceセグメントのオフセット20でint値を読み取ろうとすると例外が発生します。 元のセグメントの「一時的な境界」は、そのスライスによって継承されます。つまり、segmentに関連付けられたスコープがaliveでなくなると、sliceにもアクセスできなくなります。

クライアントは、セグメントからStreamを取得できます。これにより、セグメント(指定された要素レイアウトに従って)をスライスするために使用でき、また、結合されていないセグメント・スライス(これを行うには、セグメントが複数のスレッドから「アクセシビリティ」である必要があります)で複数のスレッドをパラレルに処理することもできます。 次のコードを使用すると、メモリー・セグメント内のすべてのint値を並列に合計できます:

 try (Arena arena = Arena.ofShared()) {
     SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT);
     MemorySegment segment = arena.allocate(SEQUENCE_LAYOUT);
     int sum = segment.elements(ValueLayout.JAVA_INT).parallel()
                      .mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0))
                      .sum();
 }

位置合せ

メモリー・セグメントに対するアクセス操作は、セグメントの空間境界と時間境界のみでなく、操作に指定された値レイアウトの「線形拘束」によっても制約されます。 アクセス操作は、レイアウトに従って「連携」の物理メモリー内のアドレスを示すセグメント内のオフセットにのみアクセスできます。 アドレスがレイアウトの整列制約の整数倍数の場合、物理メモリー内のアドレスはレイアウトに従って「連携」になります。 たとえば、アドレス1000は8バイトの境界整列制約 (1000は8の整数倍数であるため)、4バイトの境界整列制約、および2バイトの境界整列制約に従って整列されます。一方、アドレス1004は4バイトの境界整列制約に従って整列され、2バイトの境界整列制約に従って整列されますが、8バイトの境界整列制約には整列されません。 アクセス操作は、アクセス操作のパフォーマンスに影響を与える可能性があるため、連携を尊重するために必要であり、特定の物理アドレスでどのアクセス操作を使用できるかを判断することもできます。 たとえば、VarHandleを使用した「不可分アクセス操作」操作は、整列されたアドレスでのみ許可されます。 また、連携は、アクセスされるセグメントがネイティブ・セグメントかヒープ・セグメントかに関係なく、アクセス操作に適用されます。

アクセスされるセグメントがネイティブ・セグメントである場合、物理メモリー内のそのaddressをオフセットと組み合せて、物理メモリー内の「ターゲット・アドレス」を取得できます。 次の擬似関数では、次のようになります:

boolean isAligned(MemorySegment segment, long offset, MemoryLayout layout) {
  return ((segment.address() + offset) % layout.byteAlignment()) == 0;
}
たとえば、
  • アドレス1000のネイティブ・セグメントには、8バイトの境界整列制約の下にあるオフセット0、8、16、24などでアクセスできます。これは、ターゲット・アドレス (1000, 1008, 1016, 1024)が8バイト境界整列されているためです。 ターゲット・アドレスは8バイト境界整列されないため、オフセット1-7、9-15、または17-23でのアクセスは許可されません。
  • アドレス1000のネイティブ・セグメントには、4バイトの境界整列制約の下にあるオフセット0、4、8、12などでアクセスできます。これは、ターゲット・アドレス (1000, 1004, 1008, 1012)が4バイト境界整列されているためです。 ターゲット・アドレスは4バイト境界整列されないため、オフセット1-3、5-7、または9-11でのアクセスは許可されません。
  • アドレス1000のネイティブ・セグメントは、ターゲット・アドレス (1000, 1002, 1004, 1006)が2バイト境界整列されているため、オフセット0、2、4、6などで2バイト境界整列制約の下でアクセスできます。 ターゲット・アドレスは2バイト境界整列されないため、オフセット1、3、または5でのアクセスは許可されません。
  • アドレス1004のネイティブ・セグメントには、オフセット0、4、8、12などで4バイトの境界整列制約の下でアクセスでき、オフセット0、2、4、6などでは2バイト境界整列制約の下でアクセスできます。 8バイトの境界整列制約では、オフセット4、12、20、28などでアクセスできます。
  • アドレス1006のネイティブ・セグメントには、オフセット0、2、4、6などで2バイトの境界整列制約の下でアクセスできます。 4バイトの境界整列制約では、オフセット2、6、10、14などでアクセスできます。8バイトの境界整列制約では、オフセット2、10、18、26などでアクセスできます。
  • アドレス1007のネイティブ・セグメントには、オフセット0、1、2、3などで1バイトの境界整列制約の下でアクセスできます。 2バイト境界整列制約では、オフセット1、3、5、7などでアクセスできます。4バイトの境界整列制約では、オフセット1、5、9、13などでアクセスできます。8バイト境界整列制約では、オフセット1、9、17、25などでアクセスできます。

セグメントへのアクセスに使用されるアライメント制約は、通常、セグメントに格納されているデータ構造のシェイプによって決まります。 たとえば、プログラマがネイティブ・セグメントに8バイトの値のシーケンスを格納する場合は、Arena.allocate(long, long)またはSegmentAllocator.allocate(MemoryLayout)のいずれかを使用して、8バイトの整列制約を指定してセグメントを割り当てる必要があります。 これらのファクトリは、返されるセグメントを裏付けるメモリーのオフ・ヒープ・リージョンに、8バイトの整列された開始アドレスがあることを確認します。 その後、プログラマは関心のある相殺でセグメントにアクセスできます -- 0、8、16、24など -- そのようなすべてのアクセスが合致しているという知識。

アクセスされるセグメントがヒープ・セグメントである場合、アクセスが整列されているかどうかの判断はより複雑です。 物理メモリー内のセグメントのアドレスは不明で、(ガベージ・コレクション中にセグメントが再配置されると変更される場合があります。)も固定されていません。 これは、物理メモリー内のターゲット・アドレスを特定するために、指定されたオフセットとアドレスを組み合わせることができないことを意味します。 境界整列制約alwaysは物理メモリー内のアドレスの境界整列を指しているため、ヒープ・セグメント内のオフセットが整列されているかどうかは原則的に判断できません。 たとえば、プログラマが8バイトの境界整列制約を選択し、ヒープ・セグメント内のオフセット16にアクセスしようとするとします。 ヒープ・セグメントのアドレス0が物理アドレス1000に対応している場合、ターゲット・アドレス (1016)は整列されますが、アドレス0が物理アドレス1004に対応している場合、ターゲット・アドレス (1020)は整列されません。 プログラマが選択した境界整列制約に従って整列されているターゲット・アドレスへのアクセスを許可することは望ましくありませんが、物理メモリー (例:プラットフォームの考慮事項やガベージ・コレクションの動作など)で予測どおりに整列されない可能性があります。

実際には、Javaランタイムは、各nバイト要素がnバイト整列された物理アドレス(long[]double[]を除き、ここで整列はプラットフォームに依存します(後述の説明を参照))で発生するように、配列をメモリー内に配置します。 ランタイムは、ガベージ・コレクション中に配列が再配置された場合でも、この不変条件を保持します。 アクセス操作は、ヒープ・セグメント内の指定されたオフセットが物理メモリー内の整列されたアドレスを参照しているかどうかを判定するために、この不変条件に依存します。 たとえば、

  • short[]配列の開始物理アドレスは、2バイトに整列された(e.g. 1006)になるため、2バイトに整列されたアドレス(例: 1006、1008、1010、1012など)で連続する短い要素が発生します。 short[]配列によって支えられたヒープ・セグメントには、オフセット0、2、4、6などで2バイトの境界整列制約の下でアクセスできます。 4バイトの境界整列制約下にあるどのオフセットでもセグメントにアクセスすることはできません。ターゲット・アドレスが4バイト境界整列される保証はないためです。たとえば、オフセット0は物理アドレス1006に対応し、オフセット1は物理アドレス1007に対応します。 同様に、ターゲット・アドレスが8バイト整列される保証はないため、8バイト整列制約の下ではどのオフセットでもセグメントにアクセスできません。たとえば、オフセット2は物理アドレス1008に対応しますが、オフセット4は物理アドレス1010に対応します。
  • long[]配列の開始物理アドレスは、64ビット・プラットフォームでは8バイトの境界整列 (e.g. 1000)になるため、連続する長い要素が8バイト境界整列アドレスで発生します (e.g., 1000, 1008, 1016, 1024, etc.) 64ビット・プラットフォームでは、long[]配列に支えられたヒープ・セグメントに、8バイト境界整列制約の下にあるオフセット0、8、16、24などでアクセスできます。 また、ターゲット・アドレス (1000, 1004, 1008, 1012)が4バイト境界整列されているため、4バイト境界整列制約の下でオフセット0、4、8、12などでセグメントにアクセスできます。 また、ターゲット・アドレス (e.g. 1000, 1002, 1004, 1006)が2バイト境界整列されているため、2バイト境界整列制約の下でオフセット0、2、4、6などでセグメントにアクセスできます。
  • long[]配列の開始物理アドレスは、32ビット・プラットフォームでは4バイトに整列された (e.g. 1004)になるため、連続する長い要素が4バイトに整列されたアドレスで発生するようになります (e.g., 1004, 1008, 1012, 1016, etc.) 32ビット・プラットフォームでは、ターゲット・アドレス (1004, 1008, 1012, 1016)が4バイトに整列されるため、long[]配列によって支えられたヒープ・セグメントは、オフセット0、4、8、12などで4バイト境界整列制約の下でアクセスできます。 また、ターゲット・アドレス (e.g. 1000, 1002, 1004, 1006)が2バイト境界整列されているため、2バイト境界整列制約の下でオフセット0、2、4、6などでセグメントにアクセスできます。

つまり、ヒープ・セグメントには、次の表に示すように、セグメントを支えるJava配列の要素のサイズから導出される(platform-dependent) maximumの整列があります:

ヒープ・セグメントの最大整列
配列タイプ(バッキング・リージョン) サポートされる最大アライメント (バイト単位)
boolean[] ValueLayout.JAVA_BOOLEAN.byteAlignment()
byte[] ValueLayout.JAVA_BYTE.byteAlignment()
char[] ValueLayout.JAVA_CHAR.byteAlignment()
short[] ValueLayout.JAVA_SHORT.byteAlignment()
int[] ValueLayout.JAVA_INT.byteAlignment()
float[] ValueLayout.JAVA_FLOAT.byteAlignment()
long[] ValueLayout.JAVA_LONG.byteAlignment()
double[] ValueLayout.JAVA_DOUBLE.byteAlignment()
ヒープ・セグメントには、ヒープ・セグメントに関連付けられた最大整列以下の整列を持つレイアウトを使用してのみアクセスできます。 次の例に示すように、位置合せがヒープ・セグメントに関連付けられた最大位置合せより大きいレイアウトを使用してヒープ・セグメントにアクセスしようとすると失敗します:
MemorySegment byteSegment = MemorySegment.ofArray(new byte[10]);
byteSegment.get(ValueLayout.JAVA_INT, 0); // fails: ValueLayout.JAVA_INT.byteAlignment() > ValueLayout.JAVA_BYTE.byteAlignment()
このような場合、クライアントには2つのオプションがあります。 異なる配列タイプ(e.g. long[])に支えられたヒープ・セグメントを使用すると、最大長の整列をサポートできます。 具体的には、long[]に関連付けられた最大整列は、プラットフォーム依存の値(ValueLayout.ADDRESS.byteSize()に設定)であるValueLayout.JAVA_LONG.byteAlignment()に設定されます。 つまり、long[]は、64ビット・プラットフォームでは少なくとも8バイトの境界整列を提供することが保証されていますが、32ビット・プラットフォームでは4バイトの境界整列のみが提供されます:
MemorySegment longSegment = MemorySegment.ofArray(new long[10]);
longSegment.get(ValueLayout.JAVA_INT, 0); // ok: ValueLayout.JAVA_INT.byteAlignment() <= ValueLayout.JAVA_LONG.byteAlignment()
または、「整列されていないレイアウト」を使用してアクセス操作を起動できます。 整列されていないすべてのレイアウト定数(e.g. ValueLayout.JAVA_INT_UNALIGNED)の整列制約が1に設定されています。
MemorySegment byteSegment = MemorySegment.ofArray(new byte[10]);
byteSegment.get(ValueLayout.JAVA_INT_UNALIGNED, 0); // ok: ValueLayout.JAVA_INT_UNALIGNED.byteAlignment() == ValueLayout.JAVA_BYTE.byteAlignment()

長さゼロのメモリー・セグメント

「外部関数」と対話する場合は、これらの関数がメモリー・リージョンを割り当て、そのリージョンへのポインタを返すことが一般的です。 Javaランタイムはリージョンのサイズを把握できないため、メモリー・セグメントを使用してメモリー領域をモデリングすることは困難です。 ポインタに格納されている、リージョンの先頭のアドレスのみを使用できます。 たとえば、戻り型がchar*のC関数は、単一のchar値を含むリージョン、または配列のサイズを別々のパラメータに指定できるchar値の配列を含むリージョンへのポインタを返す場合があります。 配列のサイズは、外部関数をコールするコードにすぐには見えず、その結果を使用します。 外部関数から返されたポインタをバッキングするメモリーのリージョンのサイズに関するインサイトがないことに加えて、それを割り当てた外部関数によるメモリーの当該領域に対する有効期間に関するインサイトもありません。

MemorySegment APIでは、「長さゼロのメモリー・セグメント」を使用して次のものを表します:

長さゼロのセグメントのアドレスは、ポインタに格納されているアドレスです。 長さゼロのセグメントの空間的境界と時間的境界は次のとおりです:
  • セグメントのサイズはゼロです。 これらのセグメントにアクセスしようとすると、IndexOutOfBoundsExceptionで失敗します。 これは重要な安全機能です: これらのセグメントは、サイズが不明なメモリー・リージョンに関連付けられているため、これらのセグメントに関連するアクセス操作は検証できません。 実際には、長さゼロのメモリー・セグメントはアドレスを「ラップ」し、明示的なインテント(下記参照)がないと使用できません
  • セグメントはグローバル・スコープに関連付けられています。 したがって、長さゼロのメモリー・セグメントには直接アクセスできませんが、外部関数を受け入れるほかのポインタに誤って渡すことができます。

クライアントが長さがゼロのメモリー・セグメントでどのように機能するかを示すために、一部のメモリー・セグメントからポインタを読み取るクライアントの場合について考えてみます。 これは、get(AddressLayout, long)アクセス・メソッドを介して実行できます。 このメソッドは、読み取るポインタのレイアウトであるアドレス・レイアウト (e.g. ValueLayout.ADDRESS)を受け入れます。 たとえば、64ビット・プラットフォームでは、アドレス・レイアウトのサイズは8バイトです。 アクセス操作では、ポインタが格納される位置(メモリー・セグメントの先頭からの相対)を示すオフセット(バイト単位)も受け入れられます。 アクセス操作は、長さがゼロのネイティブ・メモリー・セグメントを返します。このセグメントは、開始アドレスが、指定されたオフセットで読み取られた64ビット値であるメモリーのリージョンによって支えられます。

返された長さゼロのメモリー・セグメントには、クライアントから直接アクセスできません: セグメントのサイズがゼロであるため、アクセス操作によって範囲外アクセスが発生します。 かわりに、クライアントは「不安」で、長さゼロのメモリー・セグメントに新しい空間境界を割り当てる必要があります。 これは、次のようにreinterpret(long)RESTRICTEDメソッドを使用して実行できます:

 MemorySegment z = segment.get(ValueLayout.ADDRESS, ...);   // size = 0
 MemorySegment ptr = z.reinterpret(16);                     // size = 16
 int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3);           // ok

場合によっては、クライアントは、新しい時間的境界を0長のメモリー・セグメントに割り当てることもできます。 これは、reinterpret(long, Arena, Consumer)RESTRICTEDメソッドを使用して実行できます。このメソッドは、指定されたアリーナと同じサイズおよび時間的境界を持つ新しいネイティブ・セグメントを返します:

 MemorySegment ptr = null;
 try (Arena arena = Arena.ofConfined()) {
       MemorySegment z = segment.get(ValueLayout.ADDRESS, ...);    // size = 0, scope = always alive
       ptr = z.reinterpret(16, arena, null);                       // size = 4, scope = arena.scope()
       int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3);            // ok
 }
 int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3);                  // throws IllegalStateException
あるいは、長さゼロのメモリー・セグメントをバッキングするメモリー・リージョンのサイズが静的にわかっている場合、クライアントはポインタの読取り時に使用されるアドレス・レイアウトで「ターゲット・レイアウト」RESTRICTEDをオーバーレイできます。 次に、ターゲット・レイアウトを使用して、アクセス操作によって返されるネイティブ・メモリー・セグメントのサイズを動的に「拡張」し、セグメントのサイズがターゲット・レイアウトのサイズと同じになるようにします。 つまり、返されるセグメントは長さがゼロのメモリー・セグメントではなくなり、それが表すポインタを直接参照解除できます:
 AddressLayout intArrPtrLayout = ValueLayout.ADDRESS.withTargetLayout(
         MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_INT)); // layout for int (*ptr)[4]
 MemorySegment ptr = segment.get(intArrPtrLayout, ...);         // size = 16
 int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3);               // ok

長さゼロのメモリー・セグメント(reinterpret(long)RESTRICTEDreinterpret(Arena, Consumer)RESTRICTEDreinterpret(long, Arena, Consumer)RESTRICTEDおよびAddressLayout.withTargetLayout(MemoryLayout)RESTRICTED)の操作に使用できるすべてのメソッドは、制限付きメソッドであり、注意して使用する必要があります: セグメントに不正な空間境界または時間境界(あるいはその両方)を割り当てると、メモリー・セグメントにアクセスしようとしたときにVMがクラッシュする可能性があります。

実装要件:
このインタフェースの実装は不変、スレッド・セーフ、およびvalue-basedです。
導入されたバージョン:
22