ファイル内のメモリー領域を使用したメモリー・セグメントのバッキング
ファイル内のメモリー領域は、メモリー・セグメントを支えることができます。これを行うには、FileChannel::mapメソッドを使用して、ファイル・チャネルの領域をメモリー・セグメントにマップします。
次の例は、「メモリー・レイアウトおよび構造化アクセス」で説明されているものと似ています。メモリー・セグメントが作成され、10個のPoint
構造のネイティブ配列が移入されます。違いは、メモリー・セグメントがファイル・チャネル内の領域にマップされることです。この例では、point-array.data
という名前のファイルを作成し、それを移入します。次に、読取り専用ファイル・チャネルでpoint-array.data
を開き、FFM APIを使用してその内容を取得します。
public class MemoryMappingExample {
static final SequenceLayout ptsLayout
= MemoryLayout.sequenceLayout(10,
MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("x"),
ValueLayout.JAVA_INT.withName("y")));
static final VarHandle xHandle
= ptsLayout.varHandle(
PathElement.sequenceElement(),
PathElement.groupElement("x"));
static final VarHandle yHandle
= ptsLayout.varHandle(
PathElement.sequenceElement(),
PathElement.groupElement("y"));
public static void main(String[] args) {
MemoryMappingExample myApp = new MemoryMappingExample();
try {
Files.deleteIfExists(Paths.get("point-array.data"));
myApp.createFile();
myApp.readFile();
} catch (Exception e) {
e.printStackTrace();
}
}
void createFile() throws Exception {
try (var fc = FileChannel.open(Path.of("point-array.data"),
Set.of(CREATE, READ, WRITE));
Arena arena = Arena.ofConfined()) {
MemorySegment mapped = fc.map(READ_WRITE, 0L, ptsLayout.byteSize(), arena);
System.out.println("Empty mapped segment:");
System.out.println(toHex(mapped));
for (int i = 0; i < ptsLayout.elementCount(); i++) {
int xValue = i;
int yValue = i * 10;
xHandle.set(mapped, 0L, (long) i, xValue);
yHandle.set(mapped, 0L, (long) i, yValue);
}
System.out.println("Populated mapped segment:");
System.out.println(toString(mapped));
System.out.println("Populated mapped segment in hex:");
System.out.println(toHex(mapped));
}
}
void readFile() throws Exception {
try (var fc = FileChannel.open(Path.of("point-array.data"),
Set.of(SPARSE, READ));
Arena arena = Arena.ofConfined()) {
MemorySegment mapped = fc.map(READ_ONLY, 0L, ptsLayout.byteSize(), arena);
System.out.println("Contents of point-array.data:");
System.out.println(toString(mapped));
}
}
static String toString(MemorySegment seg) {
String outputString = "";
for (int i = 0; i < ptsLayout.elementCount(); i++) {
int xVal = (int) xHandle.get(seg, 0L, (long) i);
int yVal = (int) yHandle.get(seg, 0L, (long) i);
outputString += "(" + xVal + ", " + yVal + ")";
if ((i+1 != ptsLayout.elementCount())) outputString += "\n";
}
return outputString;
}
static String toHex(MemorySegment seg) {
String outputString = "";
HexFormat formatter = HexFormat.of();
byte[] byteArray = seg.toArray(java.lang.foreign.ValueLayout.JAVA_BYTE);
for (int i = 0; i < byteArray.length; i++) {
outputString += formatter.toHexDigits(byteArray[i]) + " ";
if ((i+1) % 8 == 0 && (i+1) % 16 != 0) {
outputString += " ";
}
if ((i+1) % 16 == 0 && (i+1) < byteArray.length) {
outputString += "\n";
}
}
return outputString;
}
}
ヒント:
パフォーマンス上の理由から、VarHandlesをstatic final
として宣言することをお薦めします。
次のように出力されます。
Empty mapped segment:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Populated mapped segment:
(0, 0)
(1, 10)
(2, 20)
(3, 30)
(4, 40)
(5, 50)
(6, 60)
(7, 70)
(8, 80)
(9, 90)
Populated mapped segment in hex:
00 00 00 00 00 00 00 00 01 00 00 00 0a 00 00 00
02 00 00 00 14 00 00 00 03 00 00 00 1e 00 00 00
04 00 00 00 28 00 00 00 05 00 00 00 32 00 00 00
06 00 00 00 3c 00 00 00 07 00 00 00 46 00 00 00
08 00 00 00 50 00 00 00 09 00 00 00 5a 00 00 00
Contents of point-array.data:
(0, 0)
(1, 10)
(2, 20)
(3, 30)
(4, 40)
(5, 50)
(6, 60)
(7, 70)
(8, 80)
(9, 90)
UNIXコマンドライン・ツールhexdump
など、別のアプリケーションでpoint-array.data
の内容を確認できます。
$ hexdump -C point-array.data
00000000 00 00 00 00 00 00 00 00 01 00 00 00 0a 00 00 00 |................|
00000010 02 00 00 00 14 00 00 00 03 00 00 00 1e 00 00 00 |................|
00000020 04 00 00 00 28 00 00 00 05 00 00 00 32 00 00 00 |....(.......2...|
00000030 06 00 00 00 3c 00 00 00 07 00 00 00 46 00 00 00 |....<.......F...|
00000040 08 00 00 00 50 00 00 00 09 00 00 00 5a 00 00 00 |....P.......Z...|
00000050
次の強調表示された文では、読取りおよび書込み用に開かれるFileChannelが作成されます。
try (var fc = FileChannel.open(Path.of("point-array.data"),
Set.of(CREATE, READ, WRITE));
Arena arena = Arena.ofConfined()) {
この文は、次のStandardOpenOptionオプションを指定します。
- CREATE: ファイルが存在しない場合は新しいファイルを作成します
- READ: 読取りアクセス用にファイルを開きます
- WRITE: 書込みアクセス用にファイルを開きます
次の文は、新しいメモリー・セグメントを作成し、それをファイル・チャネル内の領域にマップします。
MemorySegment mapped = fc.map(READ_WRITE, 0L, ptsLayout.byteSize(), arena);
この文は、FileChannel.map(FileChannel.MapMode, long, long, Arena)メソッドをコールし、次の特性を持つメモリー・セグメントを作成します。
- ファイル
point-array.data
の読取りと書込みの両方が可能 - ファイルの先頭から開始する
- ファイルと同じサイズ
- そのライフサイクルは、以前に宣言された限定アリーナによって制御される
この例では、「メモリー・レイアウトおよび構造化アクセス」で説明されている例と同じ方法で、マップされたメモリー・セグメントに移入します。
この例では、point-array.data
の内容を読み取るために、読取り専用ファイル・チャネルを作成します。
var fc = FileChannel.open(Path.of("point-array.data"),
Set.of(READ)
次に、point-array.data
にマップされる読取り専用メモリー・セグメントを作成します。
MemorySegment mapped = fc.map(READ_ONLY, 0L, ptsLayout.byteSize(), arena);