jam 0.0.1
A compacting generational garbage collector for C++26
Loading...
Searching...
No Matches
jam

C++ Arm Intel AMD Build Documentation

jam is a compacting generational garbage collector for C++26. Build lists, trees or graphs, keep roots, and let the collector reclaim what you can no longer reach. Sharing and cycles are fine. A pointer takes four bytes, and objects need no GC header or intrusive base class.

Tell us where your pointers live and we generate the walk:

#include <cassert>
#include <cstdint>
#include <new>
struct node {
ptr<node> next;
std::uint64_t data;
static constexpr auto manifest = make_manifest<node>(&node::next);
};
int main() {
heap heap{{.workers = 4}};
heap_scope scope{heap};
auto const a = mk<node>(nullptr, 42u);
auto const b = mk<node>(a, 99u);
a->next = b;
root answer = a;
assert(answer->data == 42);
assert(answer->next->next == answer.get());
}
void collect() noexcept
Collect the current heap; only registered roots remain meaningful outside it.
Definition heap.ccm:2481

Here next is a managed edge; data is just data. The two nodes form a cycle. answer keeps it alive, and collection repairs the links as objects move. Drop the root and the whole cycle becomes collectible.

Manifests compose through embedded records and arrays. You can store SIMD pointer vectors too, or supply a trace hook when the layout depends on a tag. The tracing guide and pointer vector examples pick up from there. A weak_ptr<T> follows movement without keeping its target alive. A `weak_root<T>` does the same from outside the heap and stays valid across collection. Weak associations and finalizers let a live key keep a value alive and schedule a managed cleanup action when the key dies.

There are a few things you have to get right:

  • List every managed edge in a manifest or trace hook. Unlisted fields are data; Jam cannot discover a pointer you haven't told it about.
  • Keep roots outside the heap and pointers inside it. The local a and b above are stale after collect(); get fresh pointers from answer. Raw pointers and references into the heap can also expire when allocation grows it.
  • Use the owning heap_scope when working with pointers or roots, including when destroying roots. Mixing heaps is undefined behavior. Collection can use several workers, but you must stop mutating the heap before collecting.
  • Objects move as bytes. Jam does not call their move constructors or run their destructors when reclaiming them. Self-links through ptr work; raw self-pointers don't. Cleanup needs an explicit finalizer action, not a C++ destructor. See the storage contract before choosing what to put in the heap.

You choose when to call collect(). Most calls collect only young objects; periodically one collects both generations. Writes from old objects to young ones are tracked automatically. Collection policy and sizing cover the controls when you need them.

To try it, you'll need Clang 23+, CMake 4.4+ and Ninja on macOS, Linux or Windows. The build guide has the commands. import jam.unqualified; puts the names in scope as above; use import jam; for qualified names.

The topic guides go deeper; the API reference documents the individual operations.

License and contact

See LICENSE.md for the dual BSD-2-Clause/Apache-2.0 license and individual source notices for retained upstream terms.

Contributions and bug reports are welcome through GitHub. Edward Kmett can also be reached as ekmett on Libera Chat and @kmett on Twitter/X.