Honeycomb logo

Rust · global allocator · live stats

Watch every byte,
live in the browser.

Honeycomb is a small, readable segregated free-list global allocator for Rust. Plug it in with one static, and watch exactly how much memory your program is using, live.

View on GitHub Read the design

This runs the same design as the Rust source, rebuilt in plain JavaScript so you can watch it work in the browser. Type a size, allocate it, and see which size class it lands in. Free any live block and watch the stats settle back down.

Allocations
0
Frees
0
Bytes in use
0
Peak bytes
0

Live allocations

    Filled squares are blocks handed out and not yet freed. Empty squares are free blocks already carved from a slab, sitting on the free list ready for the next request of that class. A request over 4096 bytes skips the size classes and goes straight to a large bucket, same as the real allocator handing it to the system allocator.

    How to use this playground
    Type a request size in bytes and hit Allocate; watch it round up to a size class and light up a block in that class's grid. Free any live allocation from the list on the right and see bytes in use settle back down while peak bytes holds the high-water mark. Hit Run a workload for a burst of mixed allocs and frees, or Reset to start clean. It runs the same segregated free-list design as the Rust allocator, rebuilt in JavaScript.

    The stats are real numbers, from real runs.

    The bundled honeycomb-demo binary installs Honeycomb as the global allocator, runs a mixed workload of boxes, strings, a hash map, vector growth, and one million-byte allocation, then prints the accounting before and after.

    $ cargo run --release --bin honeycomb-demo
    [start] allocations=3 frees=0 bytes_in_use=1096 peak_bytes=1096
    mid-run: 2000 small boxes, 500 strings, 1000 map entries, 1000000 big bytes
    [after workload (everything dropped)] allocations=3546 frees=3541 bytes_in_use=2184 peak_bytes=1202768

    Peak crosses a megabyte when the big buffer is live, then bytes_in_use settles back near baseline once everything is dropped. That is the accounting you also drive by hand in the playground above.

    How it works

    Every allocation is rounded up to a size class, served from a free list, and backed by pages requested from the OS. Nothing hides behind a black box.

    01

    Size classes

    Requests round up to the nearest power-of-two class, from 8 bytes to 4096 bytes, chosen to satisfy both size and alignment.

    02

    Free lists

    Each class keeps an intrusive free list threaded through the free blocks themselves. No headers, no per-block overhead.

    03

    OS backing

    When a class runs dry, Honeycomb pulls one page-aligned slab from the system allocator and carves it into fresh blocks.

    Live stats, not a guess

    Call Honeycomb::stats() anywhere in your program to see exactly what is happening, without reaching for a separate profiler.

    Allocations
    count
    Frees
    count
    Bytes in use
    live
    Peak bytes
    high water

    Usage

    One static, and every allocation in your program runs through Honeycomb.

    use honeycomb::Honeycomb;
    
    #[global_allocator]
    static ALLOCATOR: Honeycomb = Honeycomb::new();
    
    fn main() {
        let v: Vec<u8> = vec![0; 1024];
        drop(v);
        println!("{:?}", ALLOCATOR.stats());
    }

    Why use it

    Honest note. jemalloc and mimalloc are faster. They are also black boxes tuned by years of production traffic. Honeycomb is not trying to win a benchmark. It exists so you can read the entire allocation path in one file, and so it can tell you, live, how much memory is actually in use. It is a teaching and observability allocator, not a production speed replacement.