How to Manage Dear ImGui Memory Usage: Custom Allocators and Debug Tools

Dear ImGui allows you to control memory usage by replacing its default malloc/free wrappers with custom allocator functions via ImGui::SetAllocatorFunctions, enabling pool allocation, tracking, and integration with engine memory systems.

Every allocation in Dear ImGui routes through configurable function pointers rather than hard-coded system calls. By default, the library uses simple wrappers around standard malloc and free defined in imgui.cpp, but you can override these hooks to optimize for real-time constraints, reduce fragmentation, or integrate with your engine's memory management. This guide covers the implementation details in the ocornut/imgui repository and provides practical strategies for monitoring and controlling memory consumption.

Replace the Default Allocator with Custom Hooks

Dear ImGui stores its allocation functions in the static function pointers GImAllocatorAllocFunc and GImAllocatorFreeFunc located at lines 1499–1501 of imgui.cpp. The default implementations, MallocWrapper and FreeWrapper, appear at lines 14–93 and 14–94 respectively. All internal allocations use the macros IM_ALLOC() and IM_FREE(), which resolve to ImGui::MemAlloc() and ImGui::MemFree() (lines 5168–5188).

To install a custom allocator, call ImGui::SetAllocatorFunctions (lines 4164–4169) before creating your first ImGui context:

// Custom allocation functions with user_data support
void* MyAlloc(size_t size, void* user_data) {
    // Example: allocate from a fixed-size pool
    return PoolAllocate(size);
}

void MyFree(void* ptr, void* user_data) {
    // Example: return memory to pool
    PoolFree(ptr);
}

// Install hooks early in main()
ImGui::SetAllocatorFunctions(MyAlloc, MyFree, nullptr);
ImGui::CreateContext();

You can retrieve the current hooks at any time using ImGui::GetAllocatorFunctions (lines 4170–4176). This is useful for chaining allocators or temporarily swapping implementations.

Example: Stack-Based Pool Allocator

For embedded systems or frame-based allocation, implement a linear allocator that resets each frame:

struct StackAllocator {
    uint8_t* base;
    size_t size;
    size_t offset;
};

void* StackAlloc(size_t sz, void* user) {
    StackAllocator* a = (StackAllocator*)user;
    if (a->offset + sz > a->size) return nullptr;
    void* ptr = a->base + a->offset;
    a->offset += sz;
    return ptr;
}

void StackFree(void* /*ptr*/, void* /*user*/) {
    // No-op; memory reset via offset=0 each frame
}

// Usage
static uint8_t buffer[1<<20];
StackAllocator stack = { buffer, sizeof(buffer), 0 };
ImGui::SetAllocatorFunctions(StackAlloc, StackFree, &stack);

Monitor Memory Usage with Debug Tools

When compiled without IMGUI_DISABLE_DEBUG_TOOLS, Dear ImGui tracks every allocation in the context's DebugAllocInfo structure. The counters DebugMemAllocCount and DebugMemFreeCount increment inside MemAlloc and MemFree (see DebugAllocHook calls at lines 5172–5175 and 5182–5185).

To view live statistics, call ImGui::ShowMetricsWindow():

ImGui::ShowMetricsWindow();  // Displays allocation count and total bytes

The Metrics window shows the difference between g.DebugMemAllocCount and g.DebugMemFreeCount, revealing active allocations. If these values grow continuously across frames, you have a leak in your ImGui usage patterns.

Example: Logging Allocator for Diagnostics

Redirect allocations to a log file to trace specific leaks:

void* LogAlloc(size_t sz, void* ud) {
    void* p = malloc(sz);
    fprintf((FILE*)ud, "ALLOC %zu -> %p\n", sz, p);
    return p;
}

void LogFree(void* p, void* ud) {
    fprintf((FILE*)ud, "FREE %p\n", p);
    free(p);
}

// Setup
FILE* log = fopen("imgui_mem.log", "w");
ImGui::SetAllocatorFunctions(LogAlloc, LogFree, log);

Best Practices for Different Scenarios

Short-lived UI elements (pop-ups, tooltips): Rely on the default allocator. Dear ImGui automatically frees transient widget memory at the end of each frame.

Heavy-weight resources (fonts, large vertex buffers): Load once and cache handles. Use ImGuiIO::Fonts->AddFontFromFileTTF to leverage the internal font atlas rather than repeatedly allocating custom buffers. Avoid calling ImGui::MemAlloc directly for large temporary buffers.

Real-time constraints (games, embedded systems): Implement a pool or arena allocator and call SetAllocatorFunctions before ImGui::CreateContext(). This eliminates system allocator overhead and prevents fragmentation during gameplay.

Debugging leaks: Ensure IMGUI_DEBUG_TOOLS is enabled (default in debug builds). Monitor the Metrics window or log the difference between allocation and free counts to identify unfreed memory.

Multi-threaded rendering: The allocator you provide must be thread-safe, or you must confine all Dear ImGui calls to a single thread. Dear ImGui does not synchronize allocator access internally.

Low-Level Memory Allocation Macros

Use IM_ALLOC(size) and IM_FREE(ptr) (documented at line 2597 of imgui.cpp) when you need to allocate memory that Dear ImGui will not manage. These macros ensure consistency with the active allocator:

char* buffer = (char*)IM_ALLOC(256);
strcpy(buffer, "Persistent data");
// ... use buffer ...
IM_FREE(buffer);

Critical warning: Never mix malloc/free with ImGui::MemAlloc/ImFree when using a custom allocator. Always pair allocations and deallocations through the same API to avoid crashes in custom pool implementations.

Summary

  • Dear ImGui routes all allocations through function pointers GImAllocatorAllocFunc and GImAllocatorFreeFunc stored in imgui.cpp.
  • Override the default malloc/free wrappers using ImGui::SetAllocatorFunctions() before creating any context.
  • Use IM_ALLOC() and IM_FREE() macros for manual allocations to respect the active allocator.
  • Enable debug tools to track DebugMemAllocCount and DebugMemFreeCount via the Metrics window.
  • For real-time applications, implement a pool or stack allocator to eliminate system allocation overhead and fragmentation.

Frequently Asked Questions

Can I use a custom memory pool with Dear ImGui?

Yes. Implement void* MyAlloc(size_t size, void* user_data) and void MyFree(void* ptr, void* user_data), then pass them to ImGui::SetAllocatorFunctions(). This allows integration with engine pool allocators, arena allocators, or stack-based schemes. Call this function before ImGui::CreateContext() to ensure all ImGui memory uses your pool.

How do I track memory leaks in Dear ImGui?

Compile without IMGUI_DISABLE_DEBUG_TOOLS and open the Metrics window with ImGui::ShowMetricsWindow(). The window displays active allocations by comparing DebugMemAllocCount and DebugMemFreeCount. Alternatively, install a logging allocator using SetAllocatorFunctions to write every allocation and free to a file for post-run analysis.

Is Dear ImGui's default allocator thread-safe?

The default malloc/free wrappers are only as thread-safe as the underlying C runtime library. Dear ImGui itself does not add synchronization around allocator calls. If you access ImGui from multiple threads, you must provide a thread-safe allocator or restrict all ImGui API calls to a single thread.

When should I call SetAllocatorFunctions?

Call ImGui::SetAllocatorFunctions immediately after initializing your application but before calling ImGui::CreateContext(). Setting the allocator after context creation leaves early allocations (including the context itself) using the previous allocator, potentially causing mismatched free operations when you later destroy the context.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →