runtime (gc_blocks.go): use a linked stack to scan marked objects

The blocks GC originally used a fixed-size stack to hold objects to scan.
When this stack overflowed, the GC would fully rescan all marked objects.
This could cause the GC to degrade to O(n^2) when scanning large linked data structures.

Instead of using a fixed-size stack, we now add a pointer field to the start of each object.
This pointer field is used to implement an unbounded linked stack.
This also consolidates the heap object scanning into one place, which simplifies the process.

This comes at the cost of introducing a pointer field to the start of the object, plus the cost of aligning the result.
This translates to:
- 16 bytes of overhead on x86/arm64 with the conservative collector
- 0 bytes of overhead on x86/arm64 with the precise collector (the layout field cost gets aligned up to 16 bytes anyway)
- 8 bytes of overhead on other 64-bit systems
- 4 bytes of overhead on 32-bit systems
- 2 bytes of overhead on AVR
This commit is contained in:
Nia Waldvogel
2025-11-29 19:58:03 -05:00
committed by Ron Evans
parent 887ac286ff
commit c9aa88b8ef
4 changed files with 106 additions and 104 deletions
+15 -3
View File
@@ -6,15 +6,27 @@
package runtime
const preciseHeap = false
import "unsafe"
type gcObjectScanner struct {
// gcLayout tracks pointer locations in a heap object.
// The conservative GC treats all locations as potential pointers, so this doesn't need to store anything.
type gcLayout struct {
}
func newGCObjectScanner(block gcBlock) gcObjectScanner {
// parseGCLayout stores the layout information passed to alloc into a gcLayout value.
// The conservative GC discards this information.
func parseGCLayout(layout unsafe.Pointer) gcLayout {
return gcLayout{}
}
// scanner creates a gcObjectScanner with this layout.
func (l gcLayout) scanner() gcObjectScanner {
return gcObjectScanner{}
}
type gcObjectScanner struct {
}
func (scanner *gcObjectScanner) pointerFree() bool {
// We don't know whether this object contains pointers, so conservatively
// return false.