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 yourImGuiMemAllocFuncandImGuiMemFreeFuncimplementations beforeImGui::CreateContext(). - Use
user_data: Pass allocator state (pools, tracking counters) through the opaquevoid*parameter available in both allocation and deallocation callbacks. - Centralized routing: All ImGui allocations flow through
ImGui::MemAllocandImGui::MemFreeinimgui.cpp, which dispatch to your installed function pointers. - Type definitions: Implementations must match the signatures declared in
imgui.haround line 288, including thesize_tandvoid* user_dataparameters. - 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →