From 1a4cb2032e2a7346c449205abd36fe4ed8f4db70 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:20:57 -0700 Subject: [PATCH] runtime: test container GC layouts --- testdata/gc.go | 151 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 1 deletion(-) diff --git a/testdata/gc.go b/testdata/gc.go index 456d763b4..445290cbb 100644 --- a/testdata/gc.go +++ b/testdata/gc.go @@ -1,6 +1,9 @@ package main -import "runtime" +import ( + "reflect" + "runtime" +) var xorshift32State uint32 = 1 @@ -19,6 +22,9 @@ func randuint32() uint32 { func main() { testNonPointerHeap() + testGlobalMapRoots() + testGlobalChannelRoots() + testReflectRoots() testKeepAlive() } @@ -74,3 +80,146 @@ func testKeepAlive() { var x int runtime.KeepAlive(&x) } + +type globalMapObject struct { + marker int + data [64]byte +} + +var globalMap = make(map[int]*globalMapObject) +var globalChannel chan *globalMapObject +var globalPointerSlice []*globalMapObject +var globalGCClobber any + +type globalMapLargeKey struct { + object *globalMapObject + data [129]byte +} + +type globalMapLargeValue struct { + object *globalMapObject + data [129]byte +} + +var globalLargeKeyMap = make(map[globalMapLargeKey]int) +var globalLargeValueMap = make(map[int]globalMapLargeValue) + +//go:noinline +func populateGlobalMaps() { + for i := 0; i < 32; i++ { + globalMap[i] = &globalMapObject{marker: 100 + i} + globalPointerSlice = append(globalPointerSlice, &globalMapObject{marker: 800 + i}) + } + globalLargeKeyMap[globalMapLargeKey{ + object: &globalMapObject{marker: 200}, + }] = 1 + globalLargeValueMap[0] = globalMapLargeValue{ + object: &globalMapObject{marker: 300}, + } +} + +func testGlobalMapRoots() { + populateGlobalMaps() + + runtime.GC() + for i := 0; i < 100; i++ { + globalGCClobber = new(globalMapObject) + } + runtime.GC() + + for i := 0; i < 32; i++ { + if globalMap[i].marker != 100+i { + panic("global map value was collected") + } + } + for key := range globalLargeKeyMap { + if key.object.marker != 200 { + panic("indirect global map key was collected") + } + } + if globalLargeValueMap[0].object.marker != 300 { + panic("indirect global map value was collected") + } + for i, object := range globalPointerSlice { + if object.marker != 800+i { + panic("global slice value was collected") + } + } +} + +//go:noinline +func populateGlobalChannel() { + globalChannel = make(chan *globalMapObject, 4) + globalChannel <- &globalMapObject{marker: 400} +} + +func testGlobalChannelRoots() { + populateGlobalChannel() + + runtime.GC() + for i := 0; i < 100; i++ { + globalGCClobber = new(globalMapObject) + } + runtime.GC() + + if (<-globalChannel).marker != 400 { + panic("global channel value was collected") + } +} + +type reflectRootObject struct { + marker int + child *reflectRootObject + data [128]byte +} + +type reflectMapKey struct { + object *reflectRootObject + data [129]byte +} + +type reflectMapValue struct { + object *reflectRootObject + data [129]byte +} + +type reflectRootMap map[reflectMapKey]reflectMapValue + +var globalReflectObject *reflectRootObject +var globalReflectMap reflectRootMap + +//go:noinline +func populateReflectRoots() { + value := reflect.New(reflect.TypeOf(reflectRootObject{})) + globalReflectObject = value.Interface().(*reflectRootObject) + globalReflectObject.child = &reflectRootObject{marker: 500} + + mapValue := reflect.MakeMapWithSize(reflect.TypeOf(globalReflectMap), 1) + mapValue.SetMapIndex( + reflect.ValueOf(reflectMapKey{object: &reflectRootObject{marker: 600}}), + reflect.ValueOf(reflectMapValue{object: &reflectRootObject{marker: 700}}), + ) + globalReflectMap = mapValue.Interface().(reflectRootMap) +} + +func testReflectRoots() { + populateReflectRoots() + + runtime.GC() + for i := 0; i < 100; i++ { + globalGCClobber = new(reflectRootObject) + } + runtime.GC() + + if globalReflectObject.child.marker != 500 { + panic("reflected object field was collected") + } + for key, value := range globalReflectMap { + if key.object.marker != 600 { + panic("reflected map key was collected") + } + if value.object.marker != 700 { + panic("reflected map value was collected") + } + } +}