# 🦀 heapster
[](https://crates.io/crates/heapster)
[](https://docs.rs/heapster)
[](https://gist.github.com/cheerfulstoic/d107229326a01ff0f333a1d3476e068d)
**Artisanal, pure-atomic heap telemetry for Rust.**
`heapster` is a stupid-lightweight, generic wrapper over any `GlobalAlloc` that tracks allocations, deallocations, and reallocations using pure relaxed atomics.
It is designed to be always-on, allowing you to hunt down pathological memory patterns, diff heap usage between code paths, and export raw allocator metrics to your telemetry dashboards with minimal overhead.
### Why Heapster?
Heavyweight heap profilers are great for deep-dives, but they are often too slow for production and too complex for simple CI assertions. `heapster` fills the gap:
- **Pure Atomics**: No mutexes, no thread-locals, no external viewer files. Just `AtomicUsize` with `Ordering::Relaxed`.
- **Plug-and-Play Generic**: Wrap `System`, `jemalloc`, `mimalloc`, or any custom allocator.
- **Pathology Hunting**: Logarithmic size bucketing (histograms) tells you exactly what sizes are dominating your heap.
- **Deep Realloc Tracking**: Distinguishes between reallocations that grew in-place, shrank in-place, or forced a full memory move (copying).
- **Benchmarking & Diffing**: Exposes a `measure` method so you can take clean snapshots of memory behavior around hot loops.
### Quickstart
Add `heapster` to your `Cargo.toml`. Enable the `fmt` feature if you want pretty, human-readable terminal histograms:
```TOML
[dependencies]
heapster = { version = "0.2", features = ["fmt"] }
```
Wrap your global allocator of choice (e.g., `System`) in your `main.rs` or `lib.rs`:
```Rust
use heapster::Heapster;
use std::alloc::System;
#[global_allocator]
static GLOBAL: Heapster<System> = Heapster::new(System);
fn main() {
// ... do some heavy work ...
// See what has transpired in the heap
println!("{}", GLOBAL.stats());
}
```
### Use Cases
**1. Zero-Friction Benchmarking & Diffing**
Stop guessing if a PR increased allocations. `heapster` lets you measure the heap stats of critical sections of code.
```Rust
let (result, heap_diff) = GLOBAL.measure(|| operation_to_measure());
assert!(heap_diff.alloc_count < 10, "Regression: The operation allocated too many times!");
```
**2. Catching Reallocation Thrashing**
Appending to a `Vec` or `String` without pre-allocating capacity causes the allocator to move memory around. `heapster` specifically tracks `realloc_move_count` so you can find and eliminate performance-killing memory copies.
**3. Always-On Production Metrics**
Because `heapster` is purely atomic, it is perfectly safe to leave on in production. You can easily wire `GLOBAL.stats()` into your Prometheus or Grafana `/metrics` endpoint to monitor `use_curr`, `use_max`, and allocation counts in real-time.
### Simple Histogram Output
When you enable the `fmt` feature, using `.stats()` generates a clear, human-readable overview of your program's memory behavior, including logarithmic ASCII histograms so you can visualize your allocation sizes:
```Plaintext
alloc_count: 10,949,628
alloc_avg: 2.45 KiB
dealloc_count: 10,949,372
dealloc_avg: 4.09 KiB
realloc_growth_count: 365,968
realloc_growth_avg: 49.12 KiB
realloc_shrink_count: 0
realloc_move_count: 351,933
realloc_move_avg: 7.21 KiB
use_curr: 260.39 KiB
use_max: 25.01 MiB
alloc_histogram:
[ 4 B .. 8 B): 2 â–ˆ
[ 8 B .. 16 B): 642,064 ███████████
[ 16 B .. 32 B): 155 â–ˆ
[ 32 B .. 64 B): 1,639,279 █████████████████████████████
[ 64 B .. 128 B): 1,926,643 ██████████████████████████████████
[ 128 B .. 256 B): 1,123,746 ████████████████████
[ 256 B .. 512 B): 1,284,154 ██████████████████████
[ 512 B .. 1 KiB): 2,246,658 ████████████████████████████████████████
[ 1 KiB .. 2 KiB): 1,283,935 ██████████████████████
[ 2 KiB .. 4 KiB): 160,612 ██
[ 4 KiB .. 8 KiB): 411 â–ˆ
[ 8 KiB .. 16 KiB): 320,985 █████
[ 16 KiB .. 32 KiB): 1 â–ˆ
[ 32 KiB .. 64 KiB): 1 â–ˆ
[ 64 KiB .. 128 KiB): 320,982 █████
realloc_growth_histogram:
[ 1 B .. 2 B): 16 â–ˆ
[ 2 B .. 4 B): 0
[ 4 B .. 8 B): 0
[ 8 B .. 16 B): 0
[ 16 B .. 32 B): 0
[ 32 B .. 64 B): 25,477 ███
[ 64 B .. 128 B): 14,976 â–ˆ
[ 128 B .. 256 B): 4,411 â–ˆ
[ 256 B .. 512 B): 106 â–ˆ
[ 512 B .. 1 KiB): 0
[ 1 KiB .. 2 KiB): 0
[ 2 KiB .. 4 KiB): 0
[ 4 KiB .. 8 KiB): 0
[ 8 KiB .. 16 KiB): 0
[ 16 KiB .. 32 KiB): 0
[ 32 KiB .. 64 KiB): 320,982 ████████████████████████████████████████
realloc_shrink_histogram: (empty)
```