Athena: Memory Management (Part 3)

The final entry for my Athena: Memory Management (Part 1 and Part 2), this entry will cover my code for merging unallocated blocks into a single block and splitting memory blocks into smaller blocks.

For merging unallocated blocks, I iterate through each unallocated block, and if the next block is not out of memory pool range and is unallocated, then the current memory header has its size and the size of its header added to it. By continuing after this, we repeat the same process for the merged block incase there is more free memory after it.

//
//
void MemoryHeap::MergeFreeBlocks()
{
	unsigned char * pCurrentMemory = static_cast<unsigned char *>( m_memoryPool );
 
	for( unsigned int byteIdx = 0; byteIdx < kMemoryPoolSize; )
	{
		BasicMemoryHeader * pCurrentHeader = reinterpret_cast<BasicMemoryHeader *>( &pCurrentMemory[byteIdx] );
 
		if( pCurrentHeader->usage == kBlockUsage_UnAllocated )
		{
			const unsigned int kNextHeaderOffset = byteIdx + sizeof( BasicMemoryHeader ) + pCurrentHeader;
			BasicMemoryHeader * pNextHeader = reinterpret_cast<BasicMemoryHeader *>( &pCurrentMemory[kNextHeaderOffset] );
 
			if( kNextHeaderOffset < kMemoryPoolSize && pNextHeader->usage == kBlockUsage_UnAllocated )
			{
				pCurrentHeader->size += sizeof( BasicMemoryHeader ) + pNextHeader->size;
				continue;
			}
		}
 
		byteIdx += sizeof( BasicMemoryHeader ) + pCurrentHeader->size;
	}
}

 

When splitting a memory block, first I check to see if there is enough space left over from what I’m allocating for a new header and 16 bytes worth of memory. The 16 bytes is arbitrary, any value is acceptable as long as it is greater than 0. If there isn’t enough space left over, the function exits without splitting the memory block.

However if there is, the next memory header is setup using the space that is left over after subtracting the new memory header size and the space required for the original memory block. The original memory block size is then set to the require memory block size.

//
//
void MemoryHeap::SplitMemoryBlock( BasicMemoryHeader * inBlockHeader, const unsigned int inSize )
{
	const unsigned int kRequiredForSplit = sizeof( BasicMemoryHeader ) + 16;
 
	//
	// Don't Split if it is the correct size
	if( inBlockHeader->size < ( inSize + kRequiredForSplit ) )
	{
		return;
	}
 
	//
	// Setup New Header from Split
	unsigned char * nextHeaderPointer = reinterpret_cast<unsigned char *>( inBlockHeader ) + sizeof( BasicMemoryHeader ) + inSize;
	BasicMemoryHeader * nextHeader = reinterpret_cast<BasicMemoryHeader *>( nextHeaderPointer );
	nextHeader->size = inBlockHeader->size - inSize - sizeof( BasicMemoryHeader );
	nextHeader->usage = kBlockUsage_UnAllocated;
 
	//
	// Set old header size to input size
	inBlockHeader->size = inSize;
}

 

Finally for when I run out of memory, I output a message to the debug console window, cause an assertion, and if the code attempts to continue, it will cause the application to exit immediately with a code 1.

//
//
void MemoryHeap::OutOfMemory() const
{
	printf( "Memory Heap has run out of memory.\n" );
	assert( 0 );
	exit( 1 );
}

 

Well thats my basic memory heap for the moment. It still has lots of work since it isn’t perfect, one flaw with this memory heap is that it doesn’t support aligned allocations and reallocation.