runtime: test container GC layouts

This commit is contained in:
Jake Bailey
2026-08-07 10:20:57 -07:00
committed by Ron Evans
parent 4cdb064f08
commit 1a4cb2032e
+150 -1
View File
@@ -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")
}
}
}