12.2 MemorySegment Operations and Data Access
Master efficient data access patterns with MemorySegment for high-performance memory manipulation.
Basic Data Access
Reading and Writing Primitives:
import java.lang.foreign.*;
public class PrimitiveAccess {
public void demonstrateAccess() {
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(64);
// Write primitives at different offsets
segment.set(ValueLayout.JAVA_BYTE, 0, (byte) 127);
segment.set(ValueLayout.JAVA_SHORT, 2, (short) 32000);
segment.set(ValueLayout.JAVA_INT, 4, 42);
segment.set(ValueLayout.JAVA_LONG, 8, 123456789L);
segment.set(ValueLayout.JAVA_FLOAT, 16, 3.14f);
segment.set(ValueLayout.JAVA_DOUBLE, 20, 2.718);
segment.set(ValueLayout.JAVA_CHAR, 28, 'A');
segment.set(ValueLayout.JAVA_BOOLEAN, 30, true);
// Read back
byte b = segment.get(ValueLayout.JAVA_BYTE, 0);
short s = segment.get(ValueLayout.JAVA_SHORT, 2);
int i = segment.get(ValueLayout.JAVA_INT, 4);
long l = segment.get(ValueLayout.JAVA_LONG, 8);
float f = segment.get(ValueLayout.JAVA_FLOAT, 16);
double d = segment.get(ValueLayout.JAVA_DOUBLE, 20);
char c = segment.get(ValueLayout.JAVA_CHAR, 28);
boolean bool = segment.get(ValueLayout.JAVA_BOOLEAN, 30);
System.out.printf("byte=%d, short=%d, int=%d, long=%d\n", b, s, i, l);
System.out.printf("float=%f, double=%f, char=%c, bool=%b\n", f, d, c, bool);
}
}
}
Array Access:
public class ArrayAccess {
public void intArrayAccess() {
try (Arena arena = Arena.ofConfined()) {
int[] values = {10, 20, 30, 40, 50};
// Copy Java array to native memory
MemorySegment segment = arena.allocateArray(
ValueLayout.JAVA_INT,
values
);
// Read individual elements
for (int i = 0; i < values.length; i++) {
long offset = i * ValueLayout.JAVA_INT.byteSize();
int value = segment.get(ValueLayout.JAVA_INT, offset);
System.out.println("Element " + i + ": " + value);
}
// Modify element
segment.set(ValueLayout.JAVA_INT, 2 * 4, 300); // Third element
// Copy back to Java array
int[] result = segment.toArray(ValueLayout.JAVA_INT);
System.out.println("Modified: " + java.util.Arrays.toString(result));
}
}
public void byteArrayAccess() {
try (Arena arena = Arena.ofConfined()) {
byte[] data = "Hello, MemorySegment!".getBytes();
MemorySegment segment = arena.allocateArray(
ValueLayout.JAVA_BYTE,
data
);
// Read as byte array
byte[] copy = segment.toArray(ValueLayout.JAVA_BYTE);
System.out.println("Data: " + new String(copy));
// Direct byte access
for (long i = 0; i < segment.byteSize(); i++) {
byte b = segment.get(ValueLayout.JAVA_BYTE, i);
System.out.printf("%c", (char) b);
}
System.out.println();
}
}
}
Slicing and Segmentation
Creating Slices:
public class SlicingOperations {
public void demonstrateSlicing() {
try (Arena arena = Arena.ofConfined()) {
// Allocate large buffer
MemorySegment buffer = arena.allocate(1024);
// Create slices for different regions
MemorySegment header = buffer.asSlice(0, 64);
MemorySegment data = buffer.asSlice(64, 896);
MemorySegment footer = buffer.asSlice(960, 64);
// Use slices independently
header.set(ValueLayout.JAVA_INT, 0, 0xABCD); // Magic number
data.set(ValueLayout.JAVA_LONG, 0, System.currentTimeMillis());
footer.set(ValueLayout.JAVA_INT, 0, 0x1234); // Checksum
// Slices share same memory
int magic = buffer.get(ValueLayout.JAVA_INT, 0);
System.out.println("Magic: 0x" + Integer.toHexString(magic));
}
}
public void nestedSlicing() {
try (Arena arena = Arena.ofConfined()) {
MemorySegment buffer = arena.allocate(256);
// First level slices
MemorySegment firstHalf = buffer.asSlice(0, 128);
MemorySegment secondHalf = buffer.asSlice(128, 128);
// Further slice first half
MemorySegment quarter1 = firstHalf.asSlice(0, 64);
MemorySegment quarter2 = firstHalf.asSlice(64, 64);
// All reference same underlying memory
quarter1.set(ValueLayout.JAVA_INT, 0, 100);
// Can read from buffer, firstHalf, or quarter1
int value = buffer.get(ValueLayout.JAVA_INT, 0);
System.out.println("Value: " + value); // 100
}
}
}
Copying Operations
Segment-to-Segment Copy:
public class CopyOperations {
public void segmentCopy() {
try (Arena arena = Arena.ofConfined()) {
// Source segment
MemorySegment source = arena.allocate(100);
for (int i = 0; i < 25; i++) {
source.set(ValueLayout.JAVA_INT, i * 4L, i);
}
// Destination segment
MemorySegment dest = arena.allocate(100);
// Copy entire segment
MemorySegment.copy(source, 0, dest, 0, 100);
// Verify
for (int i = 0; i < 25; i++) {
int value = dest.get(ValueLayout.JAVA_INT, i * 4L);
System.out.println("Copied[" + i + "]: " + value);
}
}
}
public void partialCopy() {
try (Arena arena = Arena.ofConfined()) {
MemorySegment source = arena.allocate(1024);
MemorySegment dest = arena.allocate(1024);
// Fill source
for (int i = 0; i < 256; i++) {
source.set(ValueLayout.JAVA_INT, i * 4L, i);
}
// Copy middle section (bytes 256-767)
MemorySegment.copy(
source, 256, // Source offset
dest, 0, // Dest offset
512 // Length in bytes
);
// Verify copied section
int firstCopied = dest.get(ValueLayout.JAVA_INT, 0);
System.out.println("First copied value: " + firstCopied); // Should be 64
}
}
}
Java Array Integration:
public class ArrayIntegration {
public void javaToNative() {
try (Arena arena = Arena.ofConfined()) {
int[] javaArray = {1, 2, 3, 4, 5};
// Allocate native memory
MemorySegment segment = arena.allocate(
ValueLayout.JAVA_INT.byteSize() * javaArray.length
);
// Copy Java array to native memory
MemorySegment.copy(
javaArray, 0,
segment, ValueLayout.JAVA_INT, 0,
javaArray.length
);
// Verify
for (int i = 0; i < javaArray.length; i++) {
int value = segment.get(ValueLayout.JAVA_INT, i * 4L);
System.out.println("Native[" + i + "]: " + value);
}
}
}
public void nativeToJava() {
try (Arena arena = Arena.ofConfined()) {
// Create and populate native memory
MemorySegment segment = arena.allocate(20); // 5 ints
for (int i = 0; i < 5; i++) {
segment.set(ValueLayout.JAVA_INT, i * 4L, (i + 1) * 10);
}
// Copy to Java array
int[] javaArray = new int[5];
MemorySegment.copy(
segment, ValueLayout.JAVA_INT, 0,
javaArray, 0,
5
);
System.out.println("Java array: " + java.util.Arrays.toString(javaArray));
}
}
}
Bulk Operations
Fill Operations:
public class BulkOperations {
public void fillSegment() {
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(1024);
// Fill with zeros
segment.fill((byte) 0);
// Fill with pattern
segment.fill((byte) 0xFF);
// Verify
byte b = segment.get(ValueLayout.JAVA_BYTE, 500);
System.out.println("Filled byte: 0x" + Integer.toHexString(b & 0xFF));
}
}
public void compareSegments() {
try (Arena arena = Arena.ofConfined()) {
MemorySegment seg1 = arena.allocate(100);
MemorySegment seg2 = arena.allocate(100);
// Fill both
seg1.fill((byte) 42);
seg2.fill((byte) 42);
// Compare using mismatch
long mismatchOffset = seg1.mismatch(seg2);
if (mismatchOffset == -1) {
System.out.println("Segments are identical");
} else {
System.out.println("First mismatch at byte: " + mismatchOffset);
}
// Modify one byte
seg2.set(ValueLayout.JAVA_BYTE, 50, (byte) 99);
mismatchOffset = seg1.mismatch(seg2);
System.out.println("Mismatch at: " + mismatchOffset); // 50
}
}
}
String Operations
String Encoding/Decoding:
public class StringOperations {
public void utf8Strings() {
try (Arena arena = Arena.ofConfined()) {
// Create UTF-8 C string
String text = "Hello, 世界! 🌍";
MemorySegment segment = arena.allocateUtf8String(text);
// Read back
String decoded = segment.getUtf8String(0);
System.out.println("Decoded: " + decoded);
// Check size (includes null terminator)
long size = segment.byteSize();
System.out.println("Segment size: " + size + " bytes");
// Verify null terminator
byte nullByte = segment.get(ValueLayout.JAVA_BYTE, size - 1);
System.out.println("Null terminator: " + (nullByte == 0));
}
}
public void stringArray() {
try (Arena arena = Arena.ofConfined()) {
String[] strings = {"First", "Second", "Third"};
// Allocate array of pointers
MemorySegment pointerArray = arena.allocate(
ValueLayout.ADDRESS.byteSize() * strings.length
);
// Allocate each string and store pointer
for (int i = 0; i < strings.length; i++) {
MemorySegment str = arena.allocateUtf8String(strings[i]);
pointerArray.set(
ValueLayout.ADDRESS,
i * ValueLayout.ADDRESS.byteSize(),
str
);
}
// Read back strings
for (int i = 0; i < strings.length; i++) {
MemorySegment strPtr = pointerArray.get(
ValueLayout.ADDRESS,
i * ValueLayout.ADDRESS.byteSize()
);
String str = strPtr.getUtf8String(0);
System.out.println("String[" + i + "]: " + str);
}
}
}
}
Memory-Mapped Files
Reading Memory-Mapped Files:
import java.nio.channels.FileChannel;
import java.nio.file.*;
public class MemoryMappedFiles {
public void readMappedFile(Path path) throws Exception {
try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) {
MemorySegment mapped = channel.map(
FileChannel.MapMode.READ_ONLY,
0,
channel.size(),
Arena.ofConfined()
);
// Read file header
int magic = mapped.get(ValueLayout.JAVA_INT, 0);
int version = mapped.get(ValueLayout.JAVA_INT, 4);
System.out.printf("Magic: 0x%X, Version: %d\n", magic, version);
// Read data efficiently - no copying
long fileSize = mapped.byteSize();
System.out.println("File size: " + fileSize + " bytes");
}
}
public void writeMappedFile(Path path, long size) throws Exception {
try (FileChannel channel = FileChannel.open(
path,
StandardOpenOption.CREATE,
StandardOpenOption.READ,
StandardOpenOption.WRITE
)) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment mapped = channel.map(
FileChannel.MapMode.READ_WRITE,
0,
size,
arena
);
// Write header
mapped.set(ValueLayout.JAVA_INT, 0, 0xABCD1234);
mapped.set(ValueLayout.JAVA_INT, 4, 1);
// Write data
for (int i = 0; i < 100; i++) {
mapped.set(ValueLayout.JAVA_INT, 8 + (i * 4L), i);
}
System.out.println("Written to memory-mapped file");
}
}
}
}
Real-World Example: Binary Protocol Parser
import java.lang.foreign.*;
import java.nio.ByteOrder;
import java.util.*;
public class ProtocolParser {
// Message structure:
// - Header (16 bytes):
// - Magic (4 bytes): 0x50524F54 ("PROT")
// - Version (2 bytes)
// - Type (2 bytes)
// - Length (4 bytes)
// - Flags (4 bytes)
// - Payload (variable)
private static final int MAGIC = 0x50524F54;
private static final int HEADER_SIZE = 16;
private static final MemoryLayout HEADER_LAYOUT = MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("magic"),
ValueLayout.JAVA_SHORT.withName("version"),
ValueLayout.JAVA_SHORT.withName("type"),
ValueLayout.JAVA_INT.withName("length"),
ValueLayout.JAVA_INT.withName("flags")
);
public static class Message {
public final short version;
public final short type;
public final int flags;
public final byte[] payload;
public Message(short version, short type, int flags, byte[] payload) {
this.version = version;
this.type = type;
this.flags = flags;
this.payload = payload;
}
@Override
public String toString() {
return String.format(
"Message[version=%d, type=%d, flags=0x%X, payload=%d bytes]",
version, type, flags, payload.length
);
}
}
/**
* Parse binary protocol message
*/
public static Message parse(byte[] data) {
try (Arena arena = Arena.ofConfined()) {
if (data.length < HEADER_SIZE) {
throw new IllegalArgumentException("Data too short for header");
}
// Copy to native memory
MemorySegment segment = arena.allocateArray(ValueLayout.JAVA_BYTE, data);
// Parse header
int magic = segment.get(ValueLayout.JAVA_INT, 0);
if (magic != MAGIC) {
throw new IllegalArgumentException(
String.format("Invalid magic: 0x%X", magic)
);
}
short version = segment.get(ValueLayout.JAVA_SHORT, 4);
short type = segment.get(ValueLayout.JAVA_SHORT, 6);
int length = segment.get(ValueLayout.JAVA_INT, 8);
int flags = segment.get(ValueLayout.JAVA_INT, 12);
if (data.length < HEADER_SIZE + length) {
throw new IllegalArgumentException("Incomplete message");
}
// Extract payload
byte[] payload = new byte[length];
MemorySegment.copy(
segment, ValueLayout.JAVA_BYTE, HEADER_SIZE,
payload, 0,
length
);
return new Message(version, type, flags, payload);
}
}
/**
* Serialize message to binary
*/
public static byte[] serialize(Message message) {
try (Arena arena = Arena.ofConfined()) {
int totalSize = HEADER_SIZE + message.payload.length;
MemorySegment segment = arena.allocate(totalSize);
// Write header
segment.set(ValueLayout.JAVA_INT, 0, MAGIC);
segment.set(ValueLayout.JAVA_SHORT, 4, message.version);
segment.set(ValueLayout.JAVA_SHORT, 6, message.type);
segment.set(ValueLayout.JAVA_INT, 8, message.payload.length);
segment.set(ValueLayout.JAVA_INT, 12, message.flags);
// Write payload
MemorySegment.copy(
message.payload, 0,
segment, ValueLayout.JAVA_BYTE, HEADER_SIZE,
message.payload.length
);
// Convert to byte array
return segment.toArray(ValueLayout.JAVA_BYTE);
}
}
/**
* Parse multiple messages from stream
*/
public static List<Message> parseStream(byte[] data) {
List<Message> messages = new ArrayList<>();
int offset = 0;
while (offset < data.length) {
if (data.length - offset < HEADER_SIZE) {
break; // Incomplete header
}
// Read length to determine message boundary
try (Arena arena = Arena.ofConfined()) {
MemorySegment headerSeg = arena.allocateArray(
ValueLayout.JAVA_BYTE,
data,
offset,
HEADER_SIZE
);
int length = headerSeg.get(ValueLayout.JAVA_INT, 8);
int messageSize = HEADER_SIZE + length;
if (offset + messageSize > data.length) {
break; // Incomplete message
}
// Extract and parse complete message
byte[] messageData = Arrays.copyOfRange(
data,
offset,
offset + messageSize
);
messages.add(parse(messageData));
offset += messageSize;
}
}
return messages;
}
// Usage example
public static void main(String[] args) {
// Create message
Message msg = new Message(
(short) 1,
(short) 100,
0x0001,
"Hello, Protocol!".getBytes()
);
// Serialize
byte[] data = serialize(msg);
System.out.println("Serialized: " + data.length + " bytes");
// Parse back
Message parsed = parse(data);
System.out.println("Parsed: " + parsed);
System.out.println("Payload: " + new String(parsed.payload));
}
}
Best Practices
1. Use Appropriate Access Methods:
// Good - typed access
segment.get(ValueLayout.JAVA_INT, offset);
// Bad - manual byte manipulation
int value = (segment.get(ValueLayout.JAVA_BYTE, offset) & 0xFF) |
((segment.get(ValueLayout.JAVA_BYTE, offset + 1) & 0xFF) << 8) |
((segment.get(ValueLayout.JAVA_BYTE, offset + 2) & 0xFF) << 16) |
((segment.get(ValueLayout.JAVA_BYTE, offset + 3) & 0xFF) << 24);
2. Check Bounds:
// Good - validate before access
if (offset + ValueLayout.JAVA_INT.byteSize() <= segment.byteSize()) {
int value = segment.get(ValueLayout.JAVA_INT, offset);
}
// Bad - can throw IndexOutOfBoundsException
int value = segment.get(ValueLayout.JAVA_INT, offset);
3. Use Bulk Operations:
// Good - efficient bulk copy
MemorySegment.copy(source, 0, dest, 0, 1024);
// Bad - byte-by-byte copy
for (int i = 0; i < 1024; i++) {
dest.set(ValueLayout.JAVA_BYTE, i, source.get(ValueLayout.JAVA_BYTE, i));
}
4. Leverage Slicing:
// Good - create views
MemorySegment header = buffer.asSlice(0, 64);
MemorySegment data = buffer.asSlice(64, buffer.byteSize() - 64);
// Bad - manual offset tracking
int headerOffset = 0;
int dataOffset = 64;
5. Handle Endianness:
// Explicit endianness when needed
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN);
These patterns enable efficient, safe memory operations for high-performance applications.