Efficient buffer management is crucial for high-performance serialization. Protostuff-core provides sophisticated buffer management through the LinkedBuffer system, which minimizes memory allocations and enables buffer reuse.
The core buffer management class that provides efficient memory allocation and reuse for serialization operations.
public final class LinkedBuffer {
public static LinkedBuffer allocate(int minimumCapacity);
public static int findBufferSize(int minCapacity);
public WriteSession clear();
public int size();
public byte[] toByteArray();
public void writeTo(OutputStream out) throws IOException;
public void writeTo(WriteSession session, OutputStream out) throws IOException;
}public static LinkedBuffer allocate(int minimumCapacity)Creates a new LinkedBuffer with the specified minimum capacity. The actual allocated size may be larger to optimize memory usage patterns.
Parameters:
minimumCapacity - Minimum buffer size in bytesUsage Example:
// Allocate buffer with default size
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
// Allocate larger buffer for big messages
LinkedBuffer largeBuffer = LinkedBuffer.allocate(64 * 1024); // 64KBpublic static int findBufferSize(int minCapacity)Calculates the optimal buffer size for a given minimum capacity, taking into account memory alignment and allocation patterns.
Usage:
int optimalSize = LinkedBuffer.findBufferSize(1000); // Returns optimized size >= 1000
LinkedBuffer buffer = LinkedBuffer.allocate(optimalSize);Manages the writing state and enables buffer reuse across multiple serialization operations.
public final class WriteSession {
public WriteSession clear();
public int size();
public byte[] toByteArray();
public void writeTo(OutputStream out) throws IOException;
}// Create reusable buffer
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
// Serialize multiple messages efficiently
for (Message message : messages) {
WriteSession session = buffer.clear(); // Reset for reuse
ProtostuffIOUtil.writeTo(outputStream, message, schema, buffer);
// Buffer is automatically reset for next iteration
}public static final int DEFAULT_BUFFER_SIZE = 512;The default buffer size provides a good balance between memory usage and performance for typical messages.
// Buffer sizing strategies
int smallBufferSize = 256; // For small messages < 1KB
int defaultBufferSize = 512; // For typical messages 1-10KB
int largeBufferSize = 4096; // For large messages > 10KB
int xlargeBufferSize = 64 * 1024; // For very large messages or batchesSmall Buffers (256-512 bytes):
Large Buffers (4KB-64KB):
public byte[] toByteArray()Converts the buffer contents to a byte array. This operation copies the data, so use stream-based operations when possible for better performance.
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
ProtostuffOutput output = new ProtostuffOutput(buffer);
// Write data to buffer
output.writeString(1, "Hello", false);
output.writeInt32(2, 42, false);
// Convert to byte array
byte[] data = buffer.toByteArray();public void writeTo(OutputStream out) throws IOException
public void writeTo(WriteSession session, OutputStream out) throws IOExceptionWrites buffer contents directly to an OutputStream without intermediate byte array allocation.
// Efficient streaming without byte array allocation
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
ProtostuffOutput output = new ProtostuffOutput(buffer);
// Serialize data
output.writeString(1, "Hello", false);
// Stream directly to output
buffer.writeTo(fileOutputStream);// Simple single-use pattern
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
byte[] data = ProtostuffIOUtil.toByteArray(message, schema, buffer);
// Buffer can be garbage collected// Efficient reuse pattern for multiple serializations
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
for (Message message : messageQueue) {
WriteSession session = buffer.clear(); // Prepare for reuse
byte[] data = ProtostuffIOUtil.toByteArray(message, schema, buffer);
sendData(data);
}LinkedBuffer instances are not thread-safe. Use separate buffer instances per thread or implement external synchronization.
// Thread-local buffer pattern
ThreadLocal<LinkedBuffer> bufferPool = ThreadLocal.withInitial(() ->
LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE)
);
// Usage in multi-threaded environment
public byte[] serialize(Message message) {
LinkedBuffer buffer = bufferPool.get();
WriteSession session = buffer.clear();
return ProtostuffIOUtil.toByteArray(message, schema, buffer);
}All output classes are designed to work with LinkedBuffer:
// ProtobufOutput with LinkedBuffer
LinkedBuffer buffer = LinkedBuffer.allocate(4096);
ProtobufOutput output = new ProtobufOutput(buffer);
// ProtostuffOutput with LinkedBuffer
ProtostuffOutput protostuffOutput = new ProtostuffOutput(buffer);
// Low-copy variants
LowCopyProtobufOutput lowCopyOutput = new LowCopyProtobufOutput(buffer);LinkedBuffer supports chaining multiple buffer segments for very large messages:
public ProtobufOutput(LinkedBuffer head, LinkedBuffer tail)// Chain buffers for large messages
LinkedBuffer head = LinkedBuffer.allocate(4096);
LinkedBuffer tail = LinkedBuffer.allocate(4096);
ProtobufOutput output = new ProtobufOutput(head, tail);Message Size Based:
// Small messages (< 1KB)
LinkedBuffer smallBuffer = LinkedBuffer.allocate(512);
// Medium messages (1-10KB)
LinkedBuffer mediumBuffer = LinkedBuffer.allocate(2048);
// Large messages (> 10KB)
LinkedBuffer largeBuffer = LinkedBuffer.allocate(8192);Throughput Based:
// High throughput, small messages
LinkedBuffer buffer = LinkedBuffer.allocate(1024);
// Low throughput, large messages
LinkedBuffer buffer = LinkedBuffer.allocate(64 * 1024);// Simple buffer pool implementation
public class BufferPool {
private final Queue<LinkedBuffer> buffers = new ConcurrentLinkedQueue<>();
private final int bufferSize;
public BufferPool(int bufferSize) {
this.bufferSize = bufferSize;
}
public LinkedBuffer acquire() {
LinkedBuffer buffer = buffers.poll();
return buffer != null ? buffer : LinkedBuffer.allocate(bufferSize);
}
public void release(LinkedBuffer buffer) {
buffer.clear();
buffers.offer(buffer);
}
}Buffer operations can throw:
OutOfMemoryError - When requested buffer size exceeds available memoryIOException - During stream write operationsIllegalArgumentException - For invalid buffer sizes