Vale's hybrid-generational memory is a new memory model that aims to combine all the best parts of existing memory strategies: easy as garbage collection, deterministic as reference counting, and as fast as borrow checking. 0
Note that hybrid-generational-memory is not implemented yet, it's still just a design.
There are three ingredients to make hybrid-generational memory work:
Hybrid-generational memory is built upon generational references. Recall:
Vale has three release modes:
Resilient mode uses hybrid-generational memory.
u48 means a 48-bit unsigned integer.
We chose 48 bits, but we could push it as high as 60 bits if we adjusted the below inlining mechanisms. 48 bits is more than enough though.
And a u16 offset to know where the generation is relative to the object, see generational references.
Use static analysis to reduce the number of liveness checks as much as possible. For example:
This static analysis only works when a nearby local holds the owning reference. The scope tethering explained further below will make it work with non-owning locals too.
The above static analysis only worked when a nearby local holds the owning reference. Now we'll make it work when a nearby local holds a non-owning reference too.
We'll add a u1 "tethered" bit to every allocation, next to the u48 generation number. A local with a non-owning reference can set this bit to 1 to keep its allocation alive. 4 Inside the scope of the local, we can skip all generation checks.
Not every non-owning local will tether. Static analysis will make a non-owning tether when it's dereferenced several times. Otherwise, it will just allow the generation checks to happen.
Someone letting go of the object's owning reference will still call its destructor, regardless of the tethered bit. If the tethered bit is 1, the destructor will not free the object. Instead, the last tethering local will free the object.
Loading from null is a memory safe operation: it's guaranteed to correctly seg-fault if we load from it.
The old tethered bit will usually be 0, but if another local is tethering the object, it could be 1 already.
Specifically, every time we allocate, we check the front of the queue to see if something's tether has expired, and if so, reuse that object. If not, move it to the back of the queue and ask generational malloc instead. Similar to a free-list!
That's basically it! There are some more things we could do to speed it up even more, using virtual memory, regions, or more static analysis, but we'll stop the explanation here.
To address some frequently asked questions:
Similar to how Pony scans all incoming and outgoing objects.
Some potential weaknesses to explore:
Presumably, we would make every generational reference have a pointer to the object, and a target generation number, and a pointer to the current generation. The LLVM pass would eliminate the latter.
Every mainstream OS has virtual memory, but WASM does not.
One day, we could write a compactor for Vale which could also help this, though its probably unnecessary.
With your help, we can launch a language with speed, safety, flexibility, and ease of use.
We’re a very small team of passionate individuals, working on this on our own and not backed by any corporation.
If you want to support our work, please consider sponsoring us on GitHub!
Those who sponsor us also get extra benefits, including:
With enough sponsorship, we can:
We have a strong track record, and during this quest we've discovered and implemented a lot of completely new techniques:
These have been successfully prototyped. With your sponsorship we can polish them, integrate them, and bring these techniques into the mainstream. 13
Our next steps are focused on making Vale more user-friendly by:
We aim to combine and add to the benefits of our favorite languages:
We need your help to make this happen!
If you're impressed by our track record and believe in the direction we're heading, please consider sponsoring us:
If you have any questions, always feel free to reach out via email, twitter, discord, or the subreddit. Cheers!
Tentatively named the Vale Software Foundation.
Generational references, the linear-aliasing model, and higher RAII are all complete, and region borrowing, fearless FFI, and perfect replayability have been successfully prototyped. Be sure to check out the experimental version of the compiler!