2 Comments
User's avatar
liate's avatar

Generational references are great! (I especially like that they're a pretty close reification of temporal memory safety: pointer/allocation + generation number vs pointer/allocation + abstract allocation id!) However, I think you missed their largest downside: unlike the more mainstream tools for guaranteeing temporal memory safety, they don't guarantee that live references are valid — they guarantee safety, but not liveness! Tracing GC'd and RC'd systems guarantee liveness trivially by construction, and substructural types and borrow checking more complicatedly guarantee liveness through type-system-like tools; generational references guarantee no use-after-free by just making all dereferencing fallible.

Again, generational references are cool, and enforcing memory safety with assertion-shaped things should be fine for anyone who uses runtime bounds-checked arrays over dependently-typed arrays or techniques like https://okmij.org/ftp/Computation/lightweight-static-guarantees.html . This is just the major thing to keep in mind when considering them, in a world where the established alternatives can't fail in valid programs.

Expand full comment
Sir Whinesalot's avatar

Yup, you're absolutely right. They're a memory *safety* solution but not a memory *management* solution.

Though for RC in particular the common issue of cycles must be considered. Detecting which generational references leaked is rather easy. Detecting where a GC cycle was created not so much (it's easy to detect there's one but not so easy to detect what caused it). So RC can't "fail" but it can leak and the leak can lead to a failure.

Me personally am a big fan of region-based memory management. I've seen some C libraries that provide regions/arenas plus a basic programmer-invoked tracing garbage collector. So instead of just completely clearing the arena when you're done with it, you instead can call collect.

This retains anything you still have references to, but only that specific region is traced (not the full heap) and there's no runtime GC (tracing and collection only happen when explicitly requested).

I'd love to see that "manual tracing GC" way of working expanded to a full language where the roots are tracked by the compiler and some regions are implicit (like one per function scope). Maybe a future post.

Expand full comment