The Mem Ops BytesAllocatorManualDefrag class is capable of allocating smaller sections (blocks) of a bigger byte array. Thus, a single, big byte array can be instantiated, and allocated into smaller sections which can be used as if they were separate byte arrays. You just need to know the start offset of the byte array section you have been allocated, and it's length, and you are good to go.
When you are done using an allocated byte array section you must explicitly free it. When you free a byte
array section, the
ByteArrayAllocatorAutoDefrag class will mark it as free, but will not
defragment the underlying byte array until you explicitly to tell it to. This is how
BytesAllocatorManualDefrag is different from the
The principle of not defragmenting free blocks immediate we refer to as deferred defragmentation.
Create a BytesAllocatorManualDefrag
To use the
BytesAllocatorManualDefrag class you must first create an instance of it.
Here is an example of creating a
BytesAllocatorManualDefrag allocator = new BytesAllocatorManualDefrag(new byte[1024 * 1024]);
Notice that you have to pass the underlying byte array as parameter to the
BytesAllocatorManualDefrag. It is this byte array which you can allocate smaller
sections (blocks) from. This way you will only ever have this underlying byte array allocated in
the JVM. Your memory consumption remains completely stable, and no garbage collection needs to take place.
Allocate a Block
In order to allocate a block (section) of bytes from the underlying byte array, you call the
allocate() method. Once allocated, no other
allocate() call can allocate the same
block until it has been freed. The
allocate() method returns the offset into the big, underlying
byte array where the allocated block starts. If no block could be allocated, -1 is returned. Here is
an example of allocating a block of 1024 bytes:
int offset = allocator.allocate(1024);
Getting a Reference to the Byte Array
You can get a reference to the underlying byte array via the
getData() method. Your allocated
block will reside at the returned offset inside this byte array. Here is how obtaining the byte array looks:
byte data = allocator.getData();
Accessing the Allocated Block
Once you have allocated a block of bytes from the
BytesAllocatorManualDefrag you can access
it via the data array, from the offset returned by
allocate() and until
offset + length - 1.
Here is an example of accessing an allocated block of bytes from a
int blockLength = 1024; int offset = allocator.allocate(blockLength); byte data = allocator.getData(); data[offset] = 1; //first byte of block data[offset + blockLength -1] = 2; //last byte of block
Notice the two last lines of the example. These two lines access the first and last byte of the allocated block. You can access all bytes from the first byte to the last byte this way.
Free a Block
Once you are finished with a memory block you must free it again. You free it using the
free() method takes the start and end offset of the block to free.
Here is an example of allocating and freeing a byte block:
int offset = allocator.allocate(1024); allocator.free(offset, offset + 1024);
Defragment Free Blocks
After freeing a number of previously allocated blocks from the underlying byte array, you will need to defragment the free blocks. Defragmenting the free blocks means joining adjacent free blocks into a single free block.
Remember, from a free block you can only allocate a byte block of the free block's size or smaller. If you allocate a smaller block from a free block, the free block is then broken up into two smaller blocks (one of which is the one just allocated). When the allocated block is freed, it is then marked internally as its own free block.
If you do not defragment the free blocks, sooner or later you will end up with a lot of small free blocks. This means, that you will be able to allocate only smaller and smaller blocks.
To defragment the free blocks you call the
defragment() method of the
BytesAllocatorManualDefrag . Here is how calling
When to Defragment Free Blocks
Using deferred defragmentation makes sense in cases where you have to respond to an incoming request as
fast as possible, and where your system will have idle time later during which it can defragment the
free blocks. It is during such idle times you should call
If your system is constantly very busy, there may never be an idle period during which to call
defragment() will most likely result in a longer pause
than if you immediately defragment free blocks like the
ByteArrayAllocatorAutoDefrag does. Thus, if your system
is constantly very busy, you might be better off using the
BytesAllocatorManualDefrag. As you can see, it depends on your system's
Full BytesAllocatorManualDefrag Example
Here is a full example showing how to allocate and free a memory block from a Mem Ops
int offset = allocator.allocate(1024); byte data = allocator.getData(); data[offset] = 1; //first byte of block data[offset + blockLength -1] = 2; //last byte of block //do something more with written bytes. allocator.free(offset, offset + 1024); allocator.defragment(); // defragment if necessary / fits