jemalloc clib Memory / Allocator

High-performance malloc replacement for PascalAI. Arena-based design with per-thread caching, ~250 size classes, transparent huge page support, heap profiling, and fine-grained statistics. Used in production by Firefox, Redis, Meta, and Rust's standard library.

ppm install jemalloc

Overview

jemalloc is a general-purpose malloc(3) replacement with emphasis on fragmentation avoidance and scalable multi-threaded concurrency. It was originally developed for FreeBSD, adopted by Firefox, and later became the default allocator for Rust. Meta uses it across their entire C++ server fleet.

The PascalAI binding exposes the full jemalloc API: raw allocation, aligned allocation, arena management, per-thread cache control, heap profiling, and statistics — all via a thin wrapper over libpai_jemalloc.so.

CLib package

Requires libjemalloc-dev. Ubuntu/Debian: sudo apt install libjemalloc-dev

Key Characteristics

FeatureDetail
Arena-basedMultiple independent arenas reduce lock contention across threads. Each arena manages its own memory independently.
Thread cachingPer-thread caches (tcaches) allow fast small allocations without any locking. Cache is flushed back to the arena when full.
~250 size classesFine-grained size classes mean very low internal fragmentation — typically <5% waste for small objects.
Huge pagesAutomatic transparent huge page support (2 MB pages) reduces TLB pressure for large working sets.
Heap profilingBuilt-in allocation profiling with jeprof--enable-prof.
StatisticsFine-grained per-arena and global stats: allocated, active, metadata, resident, mapped, retained bytes, and call counts.

Performance

jemalloc consistently outperforms the system allocator in multi-threaded workloads and reduces resident memory usage through better fragmentation management.

ScenarioImprovement
Multi-threaded contention2–10x less lock contention vs glibc malloc due to independent arenas per CPU
FragmentationTypically 10–30% less resident memory for long-running server processes
Large objectsComparable to system malloc; advantage is in small/medium object churn
Used by

Firefox (since 2007), Meta/Facebook (entire C++ backend), Redis, Apache Cassandra, HHVM, and Rust's default global allocator. This level of production validation makes jemalloc one of the most battle-tested allocators available.

vs Other Allocators

AllocatorComparison
tcmalloc (Google)Both are arena-based with thread caching. jemalloc typically achieves lower fragmentation; tcmalloc is sometimes faster for workloads with many short-lived allocations. jemalloc has better heap profiling.
mimalloc (Microsoft)mimalloc is newer and often faster in micro-benchmarks. jemalloc has years more production validation at scale and superior profiling tooling.
glibc mallocglibc malloc uses a single global lock (or a small fixed number of arenas). jemalloc scales to many more threads without contention.

Quick Start

Basic allocation and free

uses jemalloc;

{ Allocate 1024 bytes, use it, then free }
var ptr := Alloc(1024);
WriteLn('Allocated ' + IntToStr(AllocSize(ptr)) + ' usable bytes');
Free(ptr);

{ Allocate zeroed memory (safe for structs) }
var buf := AllocZero(64, 8);  { 64 elements x 8 bytes each }
FreeSized(buf, 64 * 8);      { faster free when size is known }

Aligned allocation (SIMD, DMA buffers)

uses jemalloc;

{ 64-byte alignment for AVX-512 SIMD operations }
var simdBuf := AllocAligned(64, 4096);
WriteLn('Aligned ptr: ' + IntToStr(simdBuf));
Free(simdBuf);

{ Resize an allocation (realloc-style) }
var p := Alloc(256);
p := Realloc(p, 512);
Free(p);

Arena per thread (isolate allocations)

uses jemalloc;

{ Create a dedicated arena and bind this thread to it }
var myArena := NewArena();
BindArena(myArena);
WriteLn('Thread using arena ' + IntToStr(CurrentArena()));

{ ... do work ... }

PurgeArena(myArena);   { return dirty pages to OS }
DestroyArena(myArena); { must be empty before destroy }

Dump statistics

uses jemalloc;

{ Human-readable stats to stderr }
PrintStats();

{ Machine-readable JSON }
var json := StatsJSON();
WriteLn(Copy(json, 1, 120) + '...');

{ Structured stats record }
var s := GetStats();
WriteLn('Allocated : ' + IntToStr(s.Allocated) + ' bytes');
WriteLn('Resident  : ' + IntToStr(s.Resident) + ' bytes');
WriteLn('Metadata  : ' + IntToStr(s.Metadata) + ' bytes');
WriteLn('Version   : ' + JEMallocVersion());

Types

TJEArena

An arena handle — an Integer index identifying a jemalloc arena. Returned by NewArena and CurrentArena. Pass to arena management functions.

TJEStats

Global allocator statistics snapshot returned by GetStats.

FieldTypeDescription
AllocatedInt64Bytes currently allocated by the application
ActiveInt64Bytes in active memory pages (always ≥ Allocated)
MetadataInt64Bytes consumed by jemalloc's own internal bookkeeping
ResidentInt64Bytes resident in physical RAM
MappedInt64Bytes mapped from the OS (includes reserved-but-unused)
RetainedInt64Bytes retained in virtual address space after being freed
SmallNMallocInt64Number of small allocation calls (cumulative)
SmallNDallocInt64Number of small deallocation calls (cumulative)
LargeNMallocInt64Number of large allocation calls (cumulative)
LargeNDallocInt64Number of large deallocation calls (cumulative)

TJEArenaStats

Per-arena statistics returned by GetArenaStats(arena).

FieldTypeDescription
ArenaIndexIntegerIndex of this arena
NThreadsIntegerNumber of threads currently bound to this arena
AllocatedInt64Bytes currently allocated from this arena
DirtyInt64Bytes in dirty unused pages (awaiting purge or reuse)
MuzzyInt64Bytes in muzzy pages (purged but not yet returned to OS)

Allocation API

Core allocation functions. All pointers are returned as Int64 (raw address). You are responsible for tracking pointer lifetimes — jemalloc does not perform garbage collection.

FunctionDescription
Alloc(Size)Allocate Size bytes. Equivalent to malloc. Returns pointer as Int64.
AllocZero(Count, Size)Allocate Count * Size bytes, zeroed. Equivalent to calloc.
Realloc(Ptr, NewSize)Resize an existing allocation. Returns new pointer (may differ from input).
Free(Ptr)Release memory. Equivalent to free.
FreeSized(Ptr, Size)Release memory with known size. Faster than Free — skips size lookup.
AllocAligned(Alignment, Size)Allocate with power-of-2 alignment. Use for SIMD buffers, DMA, cache-line alignment.
AllocSize(Ptr)Return the usable size of an allocation. May exceed the requested size due to size class rounding.

Arena API

Arenas are independent memory pools. Assigning each thread a dedicated arena eliminates all cross-thread locking for allocation-heavy workloads.

FunctionDescription
NewArenaCreate a new arena. Returns a TJEArena handle.
CurrentArenaReturn the arena index that the current thread is bound to.
BindArena(A)Bind the current thread to arena A. All subsequent allocations use this arena.
DestroyArena(A)Destroy an arena. The arena must be empty (all allocations freed) before calling.
PurgeArena(A)Return dirty unused pages in arena A to the OS. Reduces RSS at the cost of future page faults.
PurgeAllPurge dirty pages from all arenas. Useful before a latency-sensitive phase begins.

Thread Cache API

The per-thread cache (tcache) is jemalloc's primary mechanism for lock-free fast-path allocation. Small objects are served from the tcache without touching the arena at all.

FunctionDescription
FlushThreadCacheFlush the current thread's tcache, returning cached memory to its arena. Call before thread exit or after a burst of allocations.
SetThreadCache(Enable)Enable or disable the tcache for the current thread. Disabling forces all allocations through the arena (slower but more predictable latency).

Statistics API

jemalloc maintains detailed internal counters updated atomically. Stats collection has negligible overhead and is safe to call in production.

FunctionDescription
GetStatsReturn a TJEStats record with global byte counts and allocation call counts.
GetArenaStats(A)Return a TJEArenaStats record for a specific arena.
StatsJSONReturn the full jemalloc stats as a JSON string. Equivalent to malloc_stats_print with JSON output. Includes per-bin, per-arena, and global detail.
PrintStatsPrint human-readable stats to stderr. Useful for quick debugging.

Heap Profiling API

Heap profiling requires jemalloc built with --enable-prof. Output files are compatible with jeprof (a pprof-compatible tool) and can be visualized as flame graphs or call-graph PDFs.

FunctionDescription
StartProfilingEnable heap profiling. Samples allocations at a configurable rate (default: 512 KB).
DumpProfile(Path)Write the current heap profile to Path. Can be called multiple times to capture snapshots at different points.
StopProfilingDisable heap profiling and flush the final dump.
uses jemalloc;

StartProfiling();

{ ... run your workload ... }
DumpProfile('/tmp/heap_before.heap');

{ ... more work ... }
DumpProfile('/tmp/heap_after.heap');

StopProfiling();

{ Analyze with: jeprof --pdf ./myapp /tmp/heap_after.heap > report.pdf }

Info API

FunctionDescription
ArenaCountReturn the number of CPU arenas currently configured. Typically equals the number of CPU cores.
JEMallocVersionReturn the jemalloc version string, e.g. "5.3.0-0-g54eaed1d8b56b1aa528be3bdd1877e59c56fa90c".

Package Info

Version1.0.0
Typeclib
C Librarylibjemalloc-dev
Authorgustavo

System Requirements

libjemalloc-dev

Ubuntu/Debian:
sudo apt install libjemalloc-dev

TJEStats Fields

.Allocated — bytes in use by app
.Active — bytes in active pages
.Metadata — allocator overhead
.Resident — physical RAM usage
.Mapped — total OS mapping
.Retained — virtual space held
.SmallNMalloc — small alloc count
.SmallNDalloc — small free count
.LargeNMalloc — large alloc count
.LargeNDalloc — large free count

Functions

  • Alloc / AllocZero
  • Realloc
  • Free / FreeSized
  • AllocAligned
  • AllocSize
  • NewArena / CurrentArena
  • BindArena / DestroyArena
  • PurgeArena / PurgeAll
  • FlushThreadCache
  • SetThreadCache
  • GetStats
  • GetArenaStats
  • StatsJSON / PrintStats
  • StartProfiling
  • DumpProfile
  • StopProfiling
  • ArenaCount
  • JEMallocVersion

See Also