Tech and Media Labs
This site uses cookies to improve the user experience.


Bytes

Jakob Jenkov
Last update: 2019-11-01

The Mem Ops Bytes class represents a sequence of bytes from a byte array. It can be a sequence of bytes from a shared byte array, as obtained from one of the byte allocators BytesAllocatorAutoDefrag or BytesAllocatorManualDefrag, or a byte array created via the Java new operator.

To avoid fragmenting the Java heap with Bytes instances, the Bytes class is designed to be obtained from an ObjectPool . Thus, the Bytes object can be returned to the ObjectPool after the byte sequence it references has been freed back to the bytes allocator they were allocated from.

Creating a Bytes ObjectPool

To use the Bytes class effectively you should create a BytesAllocatorAutoDefrag, a BytesFactory and an ObjectPool . Here is how that looks:

BytesAllocatorAutoDefrag byteArrayAllocator = new BytesAllocatorAutoDefrag(new byte[1024 * 1024]);

BytesFactory bytesFactory = new BytesFactory(byteArrayAllocator);

ObjectPool<Bytes> objectPool = new ObjectPool<Bytes>(16,bytesFactory);

Obtaining a Bytes Instance

To obtain a Bytes instance from the ObjectPool you simply call the ObjectPool instance() method. Here is an example of obtaining a Bytes instance from the ObjectPool:

Bytes bytes = objectPool.instance();

Allocate Byte Sequence

Once you have obtained a Bytes instance from the ObjectPool you will need to allocate a sequence of bytes from the BytesAllocatorAutoDefrag. Here is how you allocate a byte sequence from the ByteAllocatorAutoDefrag via the Bytes instance:

bytes.allocate(1024);

This example allocates 1024 bytes from the ByteAllocatorAutoDefrag .

Free Bytes

When you are done with the byte sequence, and the Bytes instance, you need to free the byte sequence and the Bytes instance again. You do so by calling the Bytes free() method. Calling free() will free the Bytes instance back to the ObjectPool and the allocated bytes back to the BytesAllocatorAutoDefrag. Here is an example of how to free a Bytes instance including the byte sequence it has allocated:

bytes.free();

Bytes Fields

A Bytes object has several fields which you can use to access and work with the allocated byte sequence which the Bytes object represents. Each of these fields will be covered in the following sections.

data

The data Bytes field is a reference to the byte array in which the byte sequence is allocated. You need this reference in order to read bytes from, or write bytes to the allocated byte sequence. Here is an example:

byte value = bytes.data[someIndex];

bytes.data[someIndex] = 123;

startIndex

The startIndex Bytes field contains the index in the byte array referenced by the data field where your allocated byte sequence starts. The startIndex field contains the index of the first byte of this byte sequence.

length

The length Bytes field contains the length in bytes of the allocated byte sequence represented by this Bytes object.

endIndex

The endIndex Bytes field contains the first index after the last byte in the allocated byte sequence. In other words, the byte referenced by endIndex is not part of the byte sequence, but the byte just before it is.

The endIndex is equal to startIndex + length.

Also, endIndex will be equal to the startIndex of the next byte sequence allocated right after the byte sequence represented by this Bytes instance.

writeIndex

The writeIndex Bytes field is an index you can use when writing bytes into the allocated byte sequence. The writeIndex can be used to keep track of how many bytes you have already written to the byte sequence. Also, in case you don't use all of the allocated bytes in the byte sequence, the writeIndex can help you keep track of how many bytes you actually wrote into the byte sequence.

For instance, imagine you have to write a response from a server back to a client. You know the response will never exceed 1 MB, so you allocate a byte sequence of 1 MB to hold the response. The response might only end up requiring e.g. 38 KB, so only these 38 KB of the 1 MB allocated byte sequence to should be written back to the client.

The writeIndex field can then be used to keep track of the bytes written to the byte sequence. In the example above, the writeIndex will point to the first byte after the 38 KB byte written. Thus, the bytes to write back to the client are the bytes from and including startIndex to but excluding writeIndex. In other words, writeIndex is like the endIndex but of the actual bytes written, not of the whole allocated byte sequence.

readIndex

The readIndex Bytes field can be used when reading the bytes that were actually written to the allocated byte sequence. For instance, if 38 KB were written into a 1 MB allocated sequence, then only the first 38 KB of the allocated sequence should be read again. Thus, when sending back a response to a client, you will have to read those 38 KB out of the byte sequence and write them to a network socket. The readIndex can be used to help you keep track of how many bytes you have actually read, in case you are not able to read them all in one go.

Use Case Examples

In this section I will try to give you a few visual use case examples that shows how to use the Bytes class, and its fields. The use cases are actually quite similar in how they use the Bytes class, even if they are doing different things.

Receiving and Processing a Message

Imagine your code runs in a server that receives messages from clients. When a message arrives you need to store it in memory before processing it, and then you need to process it too.

First, you start with a shared byte array from which you can allocate a byte sequence to store the incoming message:


Second, you allocate a byte sequence from this shared byte array which is big enough to handle any message that is within acceptable message size limits:


Third, you write the incoming message into the allocated byte sequence, advancing the writeIndex while doing so:


Fourth, you start processing the message in the allocated byte sequence, advancing readIndex as you process more and more of the message:

Sending a Response to a Client

Imagine your code runs in a server that receives messages from clients. After processing a message you may need to send back a response to the client.

First, you start with a shared byte array from which you can allocate the byte sequence to generate the response into, before sending it back to the client:


Second, you allocate a byte sequence big enough to hold any response that is within acceptable response message size limits:


Third, you generate the response message into the allocated byte sequence, advancing the writeIndex while generating the response:


Fourth, you need to read the generated response message and send it back to the client. While reading and sending the generated response back, you advance the readIndex .

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC