Athena: Memory Management (Part 1)

After reading Object-Orientated Game Development, I came up with some ideas for managing memory pools. Basically it works like this, after first allocating a pool of memory of size X, the first header size is subtracted from this.

So a basic memory header would be the following code. It stores the size of the memory block following it and whether that memory block has been allocated. The size of this memory header is 8 bytes, 4 bytes for the size and 4 bytes for the usage. I will probably expand this later by adding memory guards, and maybe a pointer to the next free block to improve lookup times.

#ifndef BASICMEMORYHEADER_H_INCLUDED
#define BASICMEMORYHEADER_H_INCLUDED
 
//
//
enum BlockUsage
{
	kBlockUsage_UnAllocated,
	kBlockUsage_Allocated
};
 
//
//
struct BasicMemoryHeader
{
	BasicMemoryHeader()
		:
		usage( kBlockUsage_UnAllocated ),
		size( 0 )
	{
	}
 
	BlockUsage usage;
	unsigned int size;
}
 
#endif

 

Now for the heap object, it will contain my pool of memory. If I use 100,008 bytes, after the first 8 bytes of our pool are used for the first header, I will have 100,000 bytes available for allocation. I have the two main functions, ‘Alloc()‘ for allocating memory, and ‘Free()‘ for releasing allocated memory.

My other three functions are used when allocating and freeing memory. After I have freed a memory block, I’ll make a call to ‘MergeFreeBlocks()’, which will iterate through all memory blocks and merge two blocks together that are next to each other in memory.

When allocating memory, when I find a suitably sized memory block, I’ll make a call to ‘SplitMemoryBlock()‘, passing to it the memory block I found, and the amount of blocks I need allocated from it, if there is more than enough memory, at the end of my allocation size, a new memory header with the remainder of memory, while the original will be updated to my usage request.

If when allocating there is not enough memory, I’ll make a call to ‘OutOfMemory()‘.

#ifndef MEMORYHEAP_H_INCLUDED
#define MEMORYHEAP_H_INCLUDED
 
//
//
#include "BasicMemoryHeader.h"
 
//
//
class MemoryHeap
{
	//
	// Attributes
	private:
		const unsigned int kMemoryPoolSize;
		void * m_memoryPool = 0;
 
	//
	// Functions
	public:
		//
		//
		MemoryHeap();
 
		//
		//
		~MemoryHeap();
 
		//
		//
		void * Allocate( const unsigned int inSize );
 
		//
		//
		void Free( void * inPointer );
 
	private:
		//
		//
		void MergeFreeBlocks();
 
		//
		//
		void SplitMemoryBlock( BasicMemoryHeader * inBlockHeader, const unsigned int inUsageSize );
 
		//
		//
		void OutOfMemory() const;
}
 
#endif

 

This is my basic setup for memory heap after it has been created and destroyed.

I wanted to check to see if any memory hasn’t been deallocated when the destructor is called, so I iterate through all memory blocks to checked to see if they are allocated. If they are allocated, I make an assert happen.

//
//
#include "MemoryHeap.h"
 
//
//
MemoryHeap::MemoryHeap()
	:
	kMemoryPoolSize( 100008 )
{
	//
	// Allocated Memory Pool
	m_memoryPool = malloc( kMemoryPoolSize );
 
	//
	// Setup First Memory Header
	BasicMemoryHeader * firstMemoryHeader = static_cast<BasicMemoryHeader *>( m_memoryPool );
	firstMemoryHeader.size = kMemoryPoolSize - sizeof( BasicMemoryHeader );
}
 
//
//
MemoryHeap::~MemoryHeap()
{
	const unsigned char * pCurrentMemory = static_cast<unsigned char *>( m_memoryPool );
 
	//
	//
	for( unsigned int byteIdx = 0; byteIdx < kMemoryPoolSize; )
	{
		const BasicMemoryHeader * pCurrentHeader = reinterpret_cast<BasicMemoryHeader *>( &pCurrentMemory[byteIdx] );
		assert( pCurrentHeader->usage == kBlockUsage_UnAllocated );
		byteIdx += sizeof( BasicMemoryHeader ) + pCurrentHeader->size;
	}
 
	//
	//
	free( m_memoryPool );
}

 

I don’t want to get this blog entry clogged up with code, so I’ll post the allocation and free code in the next entry, and the merge / split code in the entry after.