How to Implement Custom Memory Allocators for Dear ImGui

Dear ImGui delegates all dynamic memory operations to function pointers that you can replace with custom implementations via ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext().

Dear ImGui uses a callback-based memory allocation system that lets you intercept every internal malloc and free operation. This article explains how to implement custom memory allocators for Dear ImGui by hooking into the ImGuiMemAllocFunc and ImGuiMemFreeFunc function pointers defined in imgui.h. Whether you need memory tracking, arena allocators, or integration with existing engine systems, you'll learn the exact API signatures and global hooks used in the ocornut/imgui source code.

Understanding the Allocation API

The memory allocator interface consists of two function type definitions and a user-data pointer that ImGui passes through on every allocation.

Function Pointer Types

In imgui.h (around line 288), Dear ImGui declares the allocator signatures as typedef function pointers:

typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data);
typedef void  (*ImGuiMemFreeFunc)(void* ptr, void* user_data);

Your custom allocator must match these signatures exactly. The user_data parameter allows you to pass an opaque pointer—typically a struct containing your allocator state—through to every allocation and deallocation call.

Default Implementation

If you do not install custom callbacks, ImGui falls back to standard malloc/free wrappers defined in imgui.cpp (around line 1493):

static void* MallocWrapper(size_t size, void* user_data) {
    IM_UNUSED(user_data);
    return malloc(size);
}
static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper;

All internal ImGui allocations flow through the global function pointers GImAllocatorAllocFunc and GImAllocatorFreeFunc, which default to these wrappers.

Installing Custom Allocators

You must configure your custom allocator before creating any ImGui context.

SetAllocatorFunctions

Call ImGui::SetAllocatorFunctions() early in your application initialization—specifically before ImGui::CreateContext(). The function is implemented in imgui.cpp (around line 4164):

void ImGui::SetAllocatorFunctions(
    ImGuiMemAllocFunc alloc_func, 
    ImGuiMemFreeFunc free_func, 
    void* user_data
);

The user_data pointer is stored globally and passed to your callbacks on every subsequent allocation. After this call, ImGui redirects all internal memory requests through your supplied functions.

Memory Allocation Entry Points

Once installed, your callbacks are invoked by ImGui::MemAlloc and ImGui::MemFree (defined in imgui.cpp around line 5169):

void* ImGui::MemAlloc(size_t size) {
    void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
    // ... debug tracking ...
    return ptr;
}

void ImGui::MemFree(void* ptr) {
    (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
}

These functions handle every dynamic allocation within ImGui, including font atlas storage, window state, draw lists, and string duplication.

Practical Implementation Examples

Below are three common patterns for supplying custom allocators to Dear ImGui.

Example 1: Instrumented malloc Wrapper with Bookkeeping

This implementation tracks total allocated bytes for profiling purposes:

static size_t g_total_alloc = 0;

void* MyAlloc(size_t size, void* /*user_data*/) {
    g_total_alloc += size;
    return malloc(size);
}

void MyFree(void* ptr, void* /*user_data*/) {
    free(ptr);
}

int main() {
    ImGui::SetAllocatorFunctions(MyAlloc, MyFree, nullptr);
    ImGui::CreateContext();
    // ... ImGui now uses MyAlloc/MyFree for all internal allocations
}

Example 2: Linear Arena Allocator

For frame-based allocation patterns where you reset memory wholesale each frame, use a linear arena. Note that individual frees are no-ops; you must reset the arena offset manually:

struct LinearArena {
    uint8_t* buffer;
    size_t   capacity;
    size_t   offset;
};

void* ArenaAlloc(size_t size, void* user_data) {
    LinearArena* arena = static_cast<LinearArena*>(user_data);
    if (arena->offset + size > arena->capacity) return nullptr;
    void* ptr = arena->buffer + arena->offset;
    arena->offset += size;
    return ptr;
}

void ArenaFree(void* /*ptr*/, void* /*user_data*/) {
    // No-op: memory is reclaimed by resetting arena.offset
}

// Usage
int main() {
    static uint8_t raw[4*1024*1024];
    LinearArena arena = { raw, sizeof(raw), 0 };
    
    ImGui::SetAllocatorFunctions(ArenaAlloc, ArenaFree, &arena);
    ImGui::CreateContext();
    
    // Each frame:
    // arena.offset = 0;  // Reset entire pool
}

Example 3: C++ std::pmr::memory_resource Wrapper

Integrate with C++17 polymorphic memory resources to leverage monotonic_buffer_resource or third-party allocators like tcmalloc or mimalloc:

#include <memory_resource>

void* PmrAlloc(size_t size, void* user_data) {
    return static_cast<std::pmr::memory_resource*>(user_data)->allocate(size);
}

void PmrFree(void* ptr, void* user_data) {
    // std::pmr requires size for deallocate, but ImGui doesn't track it.
    // Using monotonic_buffer_resource makes this safe.
    static_cast<std::pmr::memory_resource*>(user_data)->deallocate(ptr, 0);
}

int main() {
    std::pmr::monotonic_buffer_resource pool{
        std::malloc(8*1024*1024), 8*1024*1024
    };
    
    ImGui::SetAllocatorFunctions(PmrAlloc, PmrFree, &pool);
    ImGui::CreateContext();
}

Querying Current Allocators

To retrieve the currently installed allocator for debugging or when copying state across DLL boundaries, use ImGui::GetAllocatorFunctions() (defined in imgui.cpp around line 4171):

void ImGui::GetAllocatorFunctions(
    ImGuiMemAllocFunc* p_alloc_func, 
    ImGuiMemFreeFunc* p_free_func, 
    void** p_user_data
);

This copies the global function pointers and user data to the provided addresses, allowing you to verify which allocator is active or propagate the same allocator to another module.

Summary

  • Hook early: Call ImGui::SetAllocatorFunctions() with your ImGuiMemAllocFunc and ImGuiMemFreeFunc implementations before ImGui::CreateContext().
  • Use user_data: Pass allocator state (pools, tracking counters) through the opaque void* parameter available in both allocation and deallocation callbacks.
  • Centralized routing: All ImGui allocations flow through ImGui::MemAlloc and ImGui::MemFree in imgui.cpp, which dispatch to your installed function pointers.
  • Type definitions: Implementations must match the signatures declared in imgui.h around line 288, including the size_t and void* user_data parameters.
  • Retrieval API: Use ImGui::GetAllocatorFunctions() to inspect the current allocator configuration at runtime.

Frequently Asked Questions

When must I set the custom allocator relative to context creation?

You must call ImGui::SetAllocatorFunctions() before ImGui::CreateContext(). The library caches the function pointers during context initialization, and allocations occurring during context setup (such as font atlas building) will use whatever allocator is currently installed. Calling the setter after context creation leaves the default allocator active for the lifetime of that context.

Can I use different allocators for different ImGui contexts?

No. Dear ImGui uses global function pointers (GImAllocatorAllocFunc and GImAllocatorFreeFunc in imgui.cpp) rather than per-context allocators. All contexts within the same process share the same allocation functions. If you need isolation, implement a custom allocator that internally dispatches based on thread ID or user_data rather than replacing the global hooks per context.

How do I handle the missing size parameter in the free callback?

The ImGuiMemFreeFunc signature only receives the pointer and user_data, not the original allocation size. If your custom allocator requires size information for deallocation (as std::pmr::memory_resource does), you must track sizes externally or use allocation strategies that do not require per-block metadata, such as monotonic arenas or malloc-based wrappers that ignore the size during free.

Does ImGui allocate memory during the frame, or only during setup?

ImGui allocates memory both during initial context creation and continuously during frame execution. Operations like ImGui::Begin() for new windows, text rendering, and widget state management trigger allocations through ImGui::MemAlloc. Therefore, your custom allocator must remain valid and performant for the entire application lifetime, not just during initialization.

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 →