Memory Pool Library


Overview

This library implements a fixed-type, dynamic-size memory pool system in C++. It enhances performance by optimizing memory allocation and deallocation through memory reuse, enabling efficient memory management. All memory pool’s creation, allocation, and deallocation are handled exclusively through the CMemoryPoolManager.

For allocation, the project provides two approaches, manual memory management and automatic memory management using a custom CSharedPtr.

⚠️ Note: The CSharedPtr used in the memory pool system is a slightly modified version of the standard one.

  • Manual Memory Management:
    • Allows explicit control over a specific memory pool and immediate deallocation, making it possible to optimize performance and manage memory flexibly.
  • Automatic Memory Management:
    • Uses CSharedPtr to safely share memory across different parts of the program, automatically freeing memory when it is no longer in use.

These two methods are designed to be used selectively based on the situation, offering flexibility in memory control as needed.

View Repository


Class Descriptions

IMemoryPool Class

  • Role:
    • A base interface and abstract class for all memory pools. Since CMemoryPoolManager cannot manage CStaticMemoryPool<T> via void*, it relies on IMemoryPool to safely manage memory pools of T type.
  • Key Method:
    • virtual ~IMemoryPool():
      • Declared virtual to ensure proper destruction of derived classes like CStaticMemoryPool<T>. This allows destructors to be called correctly through a base class pointer.

CStaticMemoryPool<T> Class

  • Role:
    • A class that manages a fixed-type memory pool for objects of T type. A separate memory pool is created for each type.
  • Key Methods:
    • void ExpandPool():
      • The size of the memory pool is dynamically resized. The initial size can be set through the constructor of CStaticMemoryPool<T> via CMemoryPoolManager. The pool expands as needed, allocating new memory blocks of the initial size, and pushing the indices of free blocks onto the mFreeIdx stack.
    • T* Allocate():
      • The mFreeIdx stack, the indices of free blocks are managed, so the most recently freed or front-most block is prioritized for allocation. The pointer target is then constructed in-place using placement new, ensuring proper initialization and virtual function support. If no blocks are available, ExpandPool() is called.
    • void Deallocate(T* deallocPtr):
      • Calls the destructor on the pointer target, then frees the memory block by adding it back to the mFreeIdx stack for reuse, optimizing memory reuse and performance.

CMemoryPoolManager Class

  • Role:
    • A singleton class that manages the creation, allocation, and deallocation of all memory pools. This class is designed to create and manage memory pools of various types.
  • Key Methods:
    • bool CreatePool<T>(int initCapacity):
      • Creates a memory pool for T type. The initial capacity can be set via initCapacity. If a pool for this type already exists, it will not be created.
    • bool DeletePool<T>():
      • Deletes the memory pool of T type and frees the associated resources.
    • T* Allocate<T>():
      • Returns an available T type pointer from the memory pool of the same type. If no free block exists, the pool will expand.
    • void Deallocate<T>(T* deallocPtr):
      • The function deallocates the memory and returns the pointer to the pool for reuse. If the pool becomes unused after deallocation, it is deleted.
    • void DeallocateButKeepPool<T>(T* deallocPtr):
      • The function deallocates the memory and returns the pointer to the pool for reuse. Even if the pool becomes unused after deallocation, it remains.

Code Breakdown

Usage Example #1 - (Manual memory management)

int main()
{
	// create a memory pool with a size of 100 for the Player-type
	CMemoryPoolManager::GetInst()->CreatePool<Player>(100);
 
	// from the Player-type memory pool, return a ptr from an available memory block.
	Player* ptr = CMemoryPoolManager::GetInst()->Allocate<Player>();
 
	// return the ptr used from the memory block back to the memory pool for reuse; the pool may be `deleted` if it becomes unused after this deallocation
	CMemoryPoolManager::GetInst()->Deallocate(ptr);
 
	// Alternatively, use this to `prevent` the pool from being deleted even if it becomes unused after deallocation
	CMemoryPoolManager::GetInst()->DeallocateButKeepPool(ptr);
 
	// delete the Player-type memory pool
	CMemoryPoolManager::GetInst()->DeletePool<Player>();
 
	// delete the memory pool manager instance
	CMemoryPoolManager::DestroyInst();
 
	return 0;
}
Example demonstrating manual memory management using the CMemoryPoolManager for the Player-type memory pool.


Usage Example #2 - (Automatic memory management)

// classes that intend to use CSharedPtr must inherit from CRefCounter
int main()
{
	// create a memory pool with a size of 100 for the Player-type
	CMemoryPoolManager::GetInst()->CreatePool<Player>(100);
 
	// from the Player-type memory pool, return a ptr from an available memory block.
	Player* playerPtr = CMemoryPoolManager::GetInst()->Allocate<Player>();
 
	CSharedPtr<Player> ptr1 = playerPtr; // refCount: 1
	CSharedPtr<Player> ptr2 = playerPtr; // refCount: 2
 
	ptr1 = nullptr; // refCount: 1
	// automatically manage memory and return the memory to the pool
	ptr2 = nullptr; // refCount: 0
 
	// delete the memory pool manager instance
	CMemoryPoolManager::DestroyInst();
 
	return 0;
}
Example demonstrating automatic memory management using CSharedPtr with reference counting for the Player-type memory pool.