| CRM64Pro GDK v0.18.0 A free cross-platform game development kit built on top of SDL 3.0

Loading...
Searching...
No Matches
Memory Manager
Core modules

## Description

High-performance and thread-safe memory allocation system [v26.02.0].

CMem functional layers

### Overview

The CRM64Pro Memory Manager (CMem) provides high-performance, thread-safe functions for allocating memory blocks while helping to reduce memory fragmentation. All CRM64Pro GDK modules and external libraries (SDL3, libpng, etc.) use CMem for memory allocation and deallocation.
Client applications can also use CMem and define different module numbers for precise and specific statistics tracking.

### Key features

- High performance: Optimized memory allocation with optional low-level allocators
- Thread-safe: All allocation functions are safe for concurrent access
- Unified logging: Seamless integration with the Core Log system to prevent file locking issues
- Safe global destruction: Post-mortem safety mechanisms prevent crashes when destroying global/static STL containers at exit
- Reduced fragmentation: Memory pooling strategies minimize heap fragmentation
- Per-module statistics: Track memory usage across GDK modules, libraries and custom modules
- Verbose profiling: CSV export of allocation histograms and memory evolution over time

### Functional layers

CMem is composed of three different functional layers:

Frontend | User-facing API: alloc(), calloc(), realloc(), allocAligned(), free(), freeAligned(), setStatsLevel(), setVerboseSampleInterval(), info() and destroy()
Stats system | Per-module memory tracking, CSV profiling, and logging bridge
Low-level allocator | Pluggable backend allocator (Standard C/C++ or ltalloc)

### Logging integration

CMem utilizes a Single pipe architecture for outputting statistics:

- Active Mode: When the GDK is running, CMem routes all output through the Log Manager. This ensures thread safety and prevents file handle contention (e.g., when the log file is kept open for performance).
- Fallback Mode: If the Log Manager is destroyed before CMem (e.g., calling `CMem::destroy()` at the very end of `main()`), CMem automatically falls back to the last known log configuration (File/Console) to ensure final leak reports are not lost.

### Tracked modules

The stats system provides memory information for each of the following modules:

CRM64Pro | Modules, managers, objects, STL containers and network system
Core libraries | SDL3, TinyXML2 and zlib-ng
Audio libraries | libxmp, dr-flac, dr-mp3 and stb-vorbis
Image libraries | libpng
User modules | Custom modules (IDs 16-31) defined by the client application via setModuleName()

### Verbose profiling

In ::MSL_VERBOSE mode, two CSV files are produced:

Allocation histogram | Distribution of all allocation sizes grouped by module
Memory evolution | Time-series snapshots of memory allocation per module (default interval: 0.5 seconds, configurable via setVerboseSampleInterval())

### Low-level allocators

Two allocator backends are available. The selection affects how exact memory usage is tracked on different platforms:

Standard C/C++ | Default system allocator. Windows: Uses `_msize` for exact tracking. Linux: Uses `malloc_usable_size`. macOS: Uses `malloc_size`.
ltalloc | Modified high-performance allocator. Cross-platform exact tracking via `ltmsize`. Thread-safe with some memory overhead (see details below)

ltalloc details
The ltalloc allocator requests memory from the operating system in 1MB blocks and automatically creates memory pools for common allocation sizes:

- Pool sizes: 16B, 32B, 64B, 128B, 256B, 512B, 1KB, 2KB, 4KB, 8KB, 16KB, 32KB, 64KB, 128KB, 256KB and 512KB
- Large allocations: Requests greater than 512KB trigger a direct system call
- Overhead considerations: A single 16B allocation reserves 1MB from the OS. Requests are rounded up to the nearest power-of-two pool size (e.g., 200B uses a 256B slot, wasting 56B)

    <h3>Best practices</h3>
    <ul>
        <li>Call setStatsLevel() before any other CMem function in your main() to capture all allocation information.</li>
        <li>The stats gathering level can be modified at any point, but info() will dump based on the current active mode.</li>
        <li>Call destroy() at the very end of your application (or via <code>atexit</code>) to generate the final report.</li>
        <li><b>Context Aware Reporting:</b> The final report automatically distinguishes between actual leaks and "pending" deletions from global/static STL objects.</li>
    </ul>

    <h3>CMemAllocator allocation rule</h3>
    CRM64Pro is exception-free for engine object allocation. Classes derived from <code>CMemAllocator</code>
    must be dynamically allocated with <code>new(std::nothrow)</code> and the returned pointer must be checked
    against <code>nullptr</code>. Plain <code>new</code> and <code>new[]</code> are intentionally disabled for these
    classes, so allocation failures are handled through explicit checks and the normal CRM64Pro error-code flow.
    Deallocate objects normally with <code>delete</code> or <code>delete[]</code>.
    <br><br>
    Example:<br>
    <code>SceneObject* pObj = new(std::nothrow) MySceneObject();<br>
    if(!pObj) return nullptr;</code>

    @note CRM64Pro STL containers use the CMem allocator and are not directly compatible with standard STL containers.
    Explicit conversions are required:<br>

`CRM64Pro::string sSource = "sourceValue";
std::string sDest = sSource; // Will fail
std::string sDest = sSource.c_str(); // OK`

Warning
  The new/delete global operators are not overridden by default. However, you can enable this behavior by defining `C64_OVERRIDE_NEW` before including the headers and compiling CRM64Pro GDK. If enabled, all C++ allocations will be tracked by CMem.

## Enumerations
enum | CRM64Pro::CMem::eMemStatsLevel : Sint32 { CRM64Pro::CMem::MSL_NULL = 0 , CRM64Pro::CMem::MSL_NORMAL = 2 , CRM64Pro::CMem::MSL_HIGH = 4 , CRM64Pro::CMem::MSL_VERBOSE = 8 , CRM64Pro::CMem::MSL_DEBUG = 16 }
| Memory statistics verbosity. More...

## Functions
Sint32 | CRM64Pro::CMem::setModuleName (eMemoryModule eMM, const char *szName)
| Set the name for a custom user module.
void | CRM64Pro::CMem::setVerboseSampleInterval (Sint32 iInterval)
| Set time interval between each sample when the stats level is ::MSL_VERBOSE. By default it is set to 500ms.
void | CRM64Pro::CMem::setStatsLevel (eMemStatsLevel eMSL)
| Set the memory stats gathering level.
void | CRM64Pro::CMem::setBreakOnAlloc (Uint32 iAllocID)
| Set the Allocation ID to break on.
void * | CRM64Pro::CMem::alloc (size_t iSize, eMemoryModule eMM=CMM_C64)
| Reserve a memory block using CMem.
void * | CRM64Pro::CMem::calloc (size_t iNum, size_t iSize, eMemoryModule eMM=CMM_C64)
| Reserve a memory block using CMem and reset it to 0.
void * | CRM64Pro::CMem::realloc (void *pMem, size_t iSize, eMemoryModule eMM=CMM_C64)
| Reallocate a memory block using CMem.
void * | CRM64Pro::CMem::allocAligned (size_t iSize, size_t iAlign=CRM64PRO_CMEM_ALIGNMENT, eMemoryModule eMM=CMM_C64)
| Reserve a memory block using CMem aligned to the desired value.
void | CRM64Pro::CMem::free (void *pMem)
| Deallocate a memory block using CMem.
void | CRM64Pro::CMem::freeAligned (void *pMem)
| Deallocate a memory block using CMem.
void | CRM64Pro::CMem::destroy ()
| Releases the internal heap and displays final stats.
Sint32 | CRM64Pro::CMem::info (Sint32 iMode=1)
| Output useful information about the memory usage.

## Enumeration Type Documentation

## ◆ eMemStatsLevel

enum CRM64Pro::CMem::eMemStatsLevel : Sint32

Memory statistics verbosity.

Enumerator
MSL_NULL | Disable memory statistics gathering. Default for release builds.
MSL_NORMAL | Collect allocations per module (CRM64Pro and user). Default for debug builds.
MSL_HIGH | ::MSL_NORMAL plus deallocations and sizes logging for each module.
MSL_VERBOSE | ::MSL_HIGH plus: memory growth per module (CMem-Evolution.csv) and size histogram per module (CMem-Histogram.csv).
MSL_DEBUG | ::MSL_VERBOSE plus: Allocation ID tracking and memory dump for leak detection. Use setBreakOnAlloc() to catch specific leaks. [Debug builds only]

## Function Documentation

## ◆ setModuleName()

CRM64PRO_API Sint32 CRM64Pro::CMem::setModuleName | ( | eMemoryModule | eMM,
|  | const char * | szName )

Set the name for a custom user module.

Parameters

  eMM | ID of the user module. Must be in the range [CMM_USER_START, CMM_USER_END] (16 to 31).
  szName | Name of the custom user module. Maximum 31 characters; longer names are truncated. By default, all custom user modules are shown as "User".

Returns
  0 on success, or a negative error code on failure.

Note
  To rename a custom user module, call this method again with the same module ID.

## ◆ setVerboseSampleInterval()

CRM64PRO_API void CRM64Pro::CMem::setVerboseSampleInterval | ( | Sint32 | iInterval | ) |

Set time interval between each sample when the stats level is ::MSL_VERBOSE. By default it is set to 500ms.

Parameters

  iInterval | integer with time in milliseconds [1, 30000].

## ◆ setStatsLevel()

CRM64PRO_API void CRM64Pro::CMem::setStatsLevel | ( | eMemStatsLevel | eMSL | ) |

Set the memory stats gathering level.

Note
  The memory stats level must be set before any other call to CRM64Pro. Ideally, it should be the first line in main().

Parameters

  eMSL | Stats gathering level. See ::eMemStatsLevel for details.

## ◆ setBreakOnAlloc()

CRM64PRO_API void CRM64Pro::CMem::setBreakOnAlloc | ( | Uint32 | iAllocID | ) |

Set the Allocation ID to break on.

When the internal allocation counter matches this ID, a debugger breakpoint is triggered. Useful for tracking down the source of a specific memory leak identified by its AllocID in the MSL_DEBUG report.

Parameters

  iAllocID | The allocation ID to break on. 0 to disable (default).

Note
  This method is only functional in Debug builds.

## ◆ alloc()

CRM64PRO_API void * CRM64Pro::CMem::alloc | ( | size_t | iSize,
|  | eMemoryModule | eMM = CMM_C64 )

Reserve a memory block using CMem.

It is automatically aligned to 16 bytes.

Parameters

  iSize | size of the memory block, in bytes.
  eMM | used for storing who requested the memory for statistics results. Internal modules use [0-15]. User modules must be in range [CMM_USER_START, CMM_USER_END] (16-31).

Returns
  Owned pointer to the reserved memory block, or nullptr on failure.

Note
  The caller must release it with CMem::free().
  It is a replacement of standard malloc() function.

## ◆ calloc()

CRM64PRO_API void * CRM64Pro::CMem::calloc | ( | size_t | iNum,
|  | size_t | iSize,
|  | eMemoryModule | eMM = CMM_C64 )

Reserve a memory block using CMem and reset it to 0.

It is automatically aligned to 16 bytes.

Parameters

  iNum | number of elements to allocate.
  iSize | size of each element.
  eMM | used for storing who requested the memory for statistics results. Internal modules use [0-15]. User modules must be in range [CMM_USER_START, CMM_USER_END] (16-31).

Returns
  Owned pointer to the reserved memory block, or nullptr on failure.

Note
  The caller must release it with CMem::free().
  It is a replacement of standard calloc() function.

## ◆ realloc()

CRM64PRO_API void * CRM64Pro::CMem::realloc | ( | void * | pMem,
|  | size_t | iSize,
|  | eMemoryModule | eMM = CMM_C64 )

Reallocate a memory block using CMem.

It is automatically aligned to 16 bytes.

Parameters

  pMem | pointer to the memory area to be reallocated.
  iSize | size of the memory block, in bytes.
  eMM | used for storing who requested the memory for statistics results. Internal modules use [0-15]. User modules must be in range [CMM_USER_START, CMM_USER_END] (16-31).

Returns
  Owned pointer to the resized memory block, or nullptr on failure.

Note
  The caller must release it with CMem::free(). On success, pMem must not be used again.
  It is a replacement of standard realloc() function.

## ◆ allocAligned()

CRM64PRO_API void * CRM64Pro::CMem::allocAligned | ( | size_t | iSize,
|  | size_t | iAlign = CRM64PRO_CMEM_ALIGNMENT,
|  | eMemoryModule | eMM = CMM_C64 )

Reserve a memory block using CMem aligned to the desired value.

Parameters

  iSize | size of the memory block, in bytes.
  iAlign | specifies the alignment. Must be a valid alignment supported by the implementation. Usually a 2^n number is supported.
  eMM | used for storing who requested the memory for statistics results. Internal modules use [0-15]. User modules must be in range [CMM_USER_START, CMM_USER_END] (16-31).

Returns
  Owned pointer to the reserved memory block, or nullptr on failure.

Note
  The caller must release it with CMem::freeAligned().
  It is a replacement of standard aligned_alloc() function.

## ◆ free()

CRM64PRO_API void CRM64Pro::CMem::free | ( | void * | pMem | ) |

Deallocate a memory block using CMem.

Parameters

  pMem | pointer to a memory block previously allocated with alloc(), calloc() or realloc().

Note
  It is a replacement of standard free() function.

## ◆ freeAligned()

CRM64PRO_API void CRM64Pro::CMem::freeAligned | ( | void * | pMem | ) |

Deallocate a memory block using CMem.

Parameters

  pMem | pointer to a memory block previously allocated with allocAligned().

Note
  It is a replacement of standard free() function.

## ◆ destroy()

CRM64PRO_API void CRM64Pro::CMem::destroy | ( |  | ) |

Releases the internal heap and displays final stats.

Attention
  Only applicable when using a Low-level allocator. Otherwise, it only displays statistics.

Note
  Invalidation: As this function returns immediately all memory to the Operating System, all pointers allocated become invalid after this call.
  This method disables subsequent free() and alloc() calls. This prevents crashes when Global/Static objects are destroyed after the allocator has shutdown.
  Debuggers: Because free() is explicitly skipped for these late-destroyed objects, debuggers may report false positive leaks.

## ◆ info()

CRM64PRO_API Sint32 CRM64Pro::CMem::info | ( | Sint32 | iMode = 1 | ) |

Output useful information about the memory usage.

Parameters

  iMode | 1 for displaying extra information about the Low-level allocator (default value) or 0 for disabling it.

Returns
  0 on success, or a negative error code on failure.

Note
  The output of this function uses the mode set by the default log (::LM_NULL, ::LM_STDOUT, ::LM_FILE, ::LM_FILEAPPEND or ::LM_CONSOLE) and depends on the eMemStatsLevel level set.