Allow having separate GC heaps and implement coordinated marking between
them; this is useful for keeping wasm and js GC heaps separated with a
clear boundary.
Heap destruction uses CollectEverything to synchronously free every live
cell. Empty blocks from that pass do not need deferred madvise work.
Registering them with the global decommit worker can start a detached
thread during process teardown. LeakSanitizer can then hang while doing
its final thread scan with log_threads enabled.
Let full sweep deallocation opt out of deferred decommit registration.
Normal incremental sweeping stays on the background worker path. The
sanitizer LibGC container and visitor tests now exit with verbose LSan
thread logging enabled.
Maintain a HashTable<HeapBlock*> of live heap blocks in the Heap,
updated on block creation and destruction.
Weak containers (WeakMap, WeakSet, WeakRef, FinalizationRegistry)
now check block liveness before accessing cell memory in their
remove_dead_cells() methods. This prevents use-after-free when
blocks have been freed during incremental sweeping.
Instead of sweeping all heap blocks in one go after marking, sweep
incrementally, one block at a time, interleaved with program execution.
This significantly reduces worst-case GC pause times by spreading
sweep work across multiple smaller time slices.
Sweep is driven by two complementary mechanisms:
1. Timer-based sweeping: A 16ms repeating timer drives background
sweep work, processing blocks for up to 5ms per timer fire.
2. Allocation-directed sweeping: Each allocator sweeps its own
pending blocks before creating new ones, ensuring forward
progress even without timer events.
Each allocator maintains its own list of blocks pending sweep,
and allocators with pending work are tracked in a separate list
for efficient timer-driven sweeping.
Key implementation details:
- Newly allocated cells during sweep are marked immediately to
prevent premature collection.
- Mark bits are cleared incrementally as each block is swept,
rather than in a separate pass over the entire heap.
- Finalization and weak reference processing remain stop-the-world
since they must complete atomically before any sweeping occurs.
Instead of checking if every single cell overrides the "must survive GC"
virtual, we can make this a HeapBlock level thing.
This avoids almost an entire GC heap traversal during the mark phase.
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:
* JS::NonnullGCPtr -> GC::Ref
* JS::GCPtr -> GC::Ptr
* JS::HeapFunction -> GC::Function
* JS::CellImpl -> GC::Cell
* JS::Handle -> GC::Root
2024-11-15 14:49:20 +01:00
Renamed from Libraries/LibJS/Heap/CellAllocator.cpp (Browse further)