New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: statictmps can prevent heap objects from being collected #29068

Open
randall77 opened this Issue Dec 3, 2018 · 0 comments

Comments

Projects
None yet
1 participant
@randall77
Contributor

randall77 commented Dec 3, 2018

package main

import (
	"fmt"
	"runtime"
)

var x = [][]int{[]int{1}}

func main() {
	s := make([]int, 1e8)
	runtime.SetFinalizer(&s[0], func(p *int) { fmt.Println("finalized") })
	x[0] = s
	x = nil
	runtime.GC()
	runtime.GC()
	runtime.GC()
}

When I run this program, the finalizer never executes. The big slice will live in the heap forever.

The original state of the program has x pointing to a compiler-allocated global variable statictmp1. That variable has one []int slot in it, which in turn points to a second statictmp2 variable holding a single 1.

When we do x[0]=s, we set statictmp1 to point to the heap instead. Then when we do x = nil, statictmp1 is now unreachable. But statictmp1 now points to an object in the heap, and we still scan statictmp1 at every garbage collection, because it is a global.

If instead we allocated statictmp1 on the heap, this problem would go away. There's a tradeoff here which I'm not sure how to resolve. The current situation prioritizes fast startup and preferring global data over heap data, but it can result in imprecise retention of objects.

A better fix would be to include a global "object" in the GC marking phase for each global variable. (This is similar to how stack objects work.) Named globals would be in the root set, but unnamed ones like the statictmps would only be live if a heap object or other live global pointed to it.

I'm not sure it is worth fixing this problem. I sort of stumbled on it while working on #29013 but I haven't seen any instances in the wild. Thought it would be worth documenting it here in case someone had a better idea or someone found an actual real-world instance.

Note that a similar situation also applies to statictmp2. Afterx[0]=s, statictmp2 is dead. We can't collect statictmp2 because it is allocated in the globals area. We could collect it if it was allocated on the heap. But because it doesn't hold a pointer to heap objects, it isn't a big deal either way. (This also has a parallel to stack objects, which we can't collect directly - we can only ignore their contents.)

@aclements @RLH

@randall77 randall77 added this to the Unplanned milestone Dec 3, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment