Class BufferedStreamOutput

java.lang.Object
java.io.OutputStream
org.elasticsearch.common.io.stream.StreamOutput
org.elasticsearch.common.io.stream.BufferedStreamOutput
All Implemented Interfaces:
Closeable, Flushable, AutoCloseable

public class BufferedStreamOutput extends StreamOutput
Adapts a raw OutputStream into a rich StreamOutput for use with Writeable instances, using a buffer.

Similar to OutputStreamStreamOutput in function, but with different performance characteristics because it requires a buffer to be acquired or allocated up-front. Apart from the costs of the buffer creation & release a BufferedStreamOutput is likely more performant than an OutputStreamStreamOutput because it writes all fields directly to its local buffer and only copies data to the underlying stream when the buffer fills up.


Wrapping a ByteArrayOutputStream

A ByteArrayOutputStream collects data in an underlying byte[] collector which typically doubles in size each time it receives a write operation which exhausts the remaining space in the existing collector. This works well if wrapped with a BufferedStreamOutput especially if the object is expected to be small enough to fit entirely into the buffer, because then there's only one slightly-oversized byte[] allocation to create the collector, plus another right-sized byte[] allocation to extract the result, assuming the buffer is not allocated afresh each time. However, subsequent flushes may need to allocate a larger collector, copying over the existing data to the new collector, so this can perform badly for larger objects. For objects larger than half the G1 region size (8MiB on a 32GiB heap) each reallocation will require a humongous allocation which can be stressful for the garbage collector, so it's better to split the bytes into several smaller pages using utilities such as BytesStreamOutput, ReleasableBytesStreamOutput or RecyclerBytesStreamOutput in those cases.

A BufferedStreamOutput around a ByteArrayOutputStream is also good if the in-memory serialized representation will have a long lifetime, because the resulting byte[] is exactly the correct size. When using other approaches such as a BytesStreamOutput, ReleasableBytesStreamOutput or RecyclerBytesStreamOutput there will be some amount of unused overhead bytes which may be particularly undesirable for long-lived objects.

An OutputStreamStreamOutput wrapper is almost certainly worse than a BufferedStreamOutput because it will make the ByteArrayOutputStream perform significantly more allocations and copies until the collecting buffer gets large enough. Most writes to a OutputStreamStreamOutput use a thread-local intermediate buffer (itself somewhat expensive) and then copy that intermediate buffer directly to the output.

Any memory allocated in this way is untracked by the org.elasticsearch.common.breaker subsystem unless the caller takes steps to add this tracking themselves.