Living Document Notice
Published 2026-09-15. The evolving architecture, revisions, and connected notes for this dispatch live in the Stax Digital Garden.
Memory Ceilings Across 50,000-Note Vaults
Summary
In a personal knowledge vault containing 50,000 notes and over 15 million words, naive heap allocations cause memory consumption to spike past 2.8 GB. Individual heap-allocated strings for tags, note titles, file paths, and wikilink targets result in allocator fragmentation and high pointer overhead.
Bosun constrains resident set size by enforcing strict string interning and arena-based memory pooling. Interning recurrent strings into contiguous 32-bit symbols and allocating temporary parse trees within arena buckets keeps total engine memory below 180 MB under full vault workloads.
String Interning with Contiguous Symbol Pools
Every standard Rust String consumes 24 bytes of stack space for pointer, capacity, and length metadata, alongside separate heap allocations. In a large vault, tag names such as #project or #reference repeat thousands of times.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Spur(u32);
pub struct StringPool {
arena: Vec<u8>,
lookup: HashMap<&'static str, Spur>,
}When the parser encounters a tag or note path, it queries the string pool. If present, it returns the existing 32-bit Spur. If absent, the string is appended to a contiguous memory buffer and indexed. Nodes store this 4-byte index, reducing metadata memory consumption by 83 percent across 50,000 documents.
String comparisons collapse into single 32-bit integer equality checks, skipping byte-by-byte string comparisons during search filters.
Arena Lifetimes for Ephemeral Parsing Structures
Generating ASTs during bulk re-indexing allocates millions of short-lived node records. Freeing each node individually places severe lock contention on standard heap allocators.
pub fn parse_in_arena<'a>(
source: &'a str,
arena: &'a bumpalo::Bump
) -> &'a AstNode<'a> {
// All child nodes allocated within contiguous bump chunks
arena.alloc(AstNode::Document(/* ... */))
}By using bump allocation arenas, node memory is allocated via pointer increments. Once index extraction completes, clearing the arena resets the pointer in a single CPU instruction without traversing individual nodes.
Because chunks in the bump arena remain allocated between document parsing cycles, the operating system kernel performs zero memory page mapping calls during batch re-indexing.
Memory Profiling with Jemalloc and DHAT
To detect hidden heap bloat, Bosun instruments native engine builds with jemalloc profiling hooks and the DHAT heap analyzer. These tools trace exact allocation locations, peak resident memory, and heap fragmentation ratios.
Profiling confirmed that replacing node string vectors with interned spur slices lowered peak heap fragmentation from 38 percent down to under 4 percent under continuous batch ingestion.
Jemalloc arena decay parameters were tuned to dirty_decay_ms: 1000 and muzzy_decay_ms: 2000, reclaiming unused memory pages quickly without triggering CPU paging thrash.
Memory Consumption Across Note Scales
The table below shows resident set size measurements comparing naive heap allocation against Bosun interned arena architectures.
| Vault Scale (Notes) | Word Count | Naive Heap RSS (MB) | Interned Arena RSS (MB) | Memory Reduction Ratio |
|---|---|---|---|---|
| 5,000 notes | 1.5M words | 280 MB | 19 MB | 14.7x reduction |
| 15,000 notes | 4.6M words | 840 MB | 54 MB | 15.5x reduction |
| 30,000 notes | 9.2M words | 1,720 MB | 108 MB | 15.9x reduction |
| 50,000 notes | 15.4M words | 2,860 MB | 176 MB | 16.2x reduction |
| 100,000 notes | 31.0M words | 5,840 MB | 345 MB | 16.9x reduction |
- Directus Target: bosunpkm-blog
- Garden Source Reference: MOC - Bosun PKM Engine, MOC - Bosun PKM Tools, MOC - The Plain-Text Longevity Standard