Heuristic:ClickHouse ClickHouse Jemalloc Production Requirement
| Knowledge Sources | |
|---|---|
| Domains | Memory_Management, Optimization |
| Last Updated | 2026-02-08 18:00 GMT |
Overview
jemalloc is the mandatory memory allocator for production ClickHouse builds, with platform-specific linking requirements on macOS and Linux-specific malloc wrapping for memory tracking instrumentation.
Description
ClickHouse uses jemalloc as its default memory allocator and warns against disabling it for production builds. The build system wraps all standard C allocation functions (`malloc`, `free`, `calloc`, `realloc`, etc.) using the linker's `--wrap` mechanism to inject memory tracking. On macOS, a special `-u_zone_register` linker flag forces jemalloc registration as the system allocator, working around a known jemalloc issue where static linking causes the allocator constructor to be dead-code-eliminated. Sanitizer builds skip malloc wrapping because sanitizers have their own allocation interceptors.
Usage
Apply this heuristic when building for production, debugging memory issues, or porting to new platforms. Key decision points: ensuring jemalloc is enabled, understanding memory tracking data, diagnosing SIGSEGV on macOS related to allocator registration, and understanding why sanitizer builds behave differently.
The Insight (Rule of Thumb)
- Action: Always enable jemalloc (`ENABLE_JEMALLOC=ON`, default) for production builds. Never disable it without understanding the consequences.
- Value: jemalloc provides better fragmentation handling, memory profiling capabilities, and consistent allocation performance compared to glibc malloc.
- Trade-off: jemalloc adds ~1MB to binary size and requires platform-specific linking workarounds.
- macOS Caveat: Must force-link `_zone_register` symbol or jemalloc silently falls back to system malloc, leading to SIGSEGV.
- Sanitizer Caveat: Malloc wrapping is disabled for sanitizer builds (ASan, TSan, etc.) because sanitizers intercept allocations independently.
- Musl Caveat: `pvalloc` wrapping is skipped on musl because musl doesn't provide this function.
Reasoning
jemalloc warning from `CMakeLists.txt:500-502`:
if (NOT ENABLE_JEMALLOC)
message (WARNING "Non default allocator is disabled. This is not recommended for production builds.")
endif ()
Malloc wrapping from `CMakeLists.txt:514-535`:
# Wrap the malloc/free and other C-style functions with our own ones
# to inject memory tracking mechanism into them.
# Sanitizers have their own way of intercepting the
# allocations and deallocations, so we skip this step for them.
if (NOT (SANITIZE OR SANITIZE_COVERAGE OR OS_DARWIN OR OS_FREEBSD OR OS_SUNOS))
target_link_options(${target} PRIVATE
"LINKER:--wrap=malloc"
"LINKER:--wrap=free"
"LINKER:--wrap=calloc"
"LINKER:--wrap=realloc"
"LINKER:--wrap=aligned_alloc"
"LINKER:--wrap=posix_memalign"
...
)
endif()
macOS jemalloc registration from `CMakeLists.txt:541-555`:
# In case of static jemalloc, because zone_register() is located in zone.c and
# is never used outside (it is declared as constructor) it is omitted
# by the linker, and so jemalloc will not be registered as system
# allocator under osx [1], and clickhouse will SIGSEGV.
#
# [1]: https://github.com/jemalloc/jemalloc/issues/708
if (ENABLE_JEMALLOC AND OS_DARWIN)
set_property(TARGET ${target} APPEND PROPERTY LINK_OPTIONS -u_zone_register)
endif()