On Wed, Nov 3, 2021 at 8:56 AM Ge <evergon...@gmail.com> wrote: > > Hi, recently I was trying to figure out how GC marks stack objects and found > some places of the implementation detail over my head. > > source: > ``` > package main > > import "runtime" > > func main() { > x := make([]byte, 256*1024*1024) > runtime.GC() ← t1 > x[0] = 2 > > runtime.GC() ← t2 > println("x released") > runtime.GC() > println("over") > } > ``` > > The GC trace info was like this: > ``` > ➜ gc GODEBUG=gctrace=1 ./largeslice > gc 1 @0.009s 0%: 0.051+0.27+0.005 ms clock, 0.40+0/0.18/0.034+0.040 ms cpu, > 256->256->256 MB, 257 MB goal, 8 P (forced) > gc 2 @0.010s 1%: 0.033+0.21+0.004 ms clock, 0.26+0/0.16/0.078+0.032 ms cpu, > 256->256->256 MB, 512 MB goal, 8 P (forced) > gc 3 @0.012s 1%: 0.019+0.091+0.003 ms clock, 0.15+0/0.15/0.047+0.024 ms cpu, > 256->256->0 MB, 512 MB goal, 8 P (forced) > x released > gc 4 @0.013s 1%: 0.014+0.11+0.004 ms clock, 0.11+0/0.13/0.058+0.036 ms cpu, > 0->0->0 MB, 4 MB goal, 8 P (forced) > over > ``` > It seems that the slice x could be garbage collected after the last use of it, > which feels so intelligent and a little confusing, since fucntion call did't > return yet, > and made me wondering how Golang dynamically identify these stack objects. > > ``` > // https://github.com/golang/go/blob/master/src/runtime/stack.go#L1246 > func getStackMap(frame *stkframe, cache *pcvalueCache, debug bool) (locals, > args bitvector, objs []stackObjectRecord) { > ... > stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps)) > ... > ``` > > AFAIK stack pointer maps are got via getStackmap function, which > reads FUNCDATA info from gopclntab(not very sure about this) section. > > My question is that will GC get different stack maps at t1 and t2 ? > And if so how Golang implemented it, as GC stack scanning could happen > at any time, which means for different pc there maybe lot of possiblites > of stack maps.
Yes, the GC will see a different map of live pointers on the stack at t1 and t2. A GC can occur at any point where a goroutine can be preempted. That means that the live pointer map has to be conservatively correct at all such points. I say conservatively correct because while it is essential that any live pointer be in the stack map, it is OK if a previously live pointer remains in the stack map even after it is no longer live. That will mean that the value stays live longer than necessary, but it otherwise doesn't matter. In practice the compiler builds stack maps that are correct at each function call. You can see the livemaps generated by the compiler if you build with -gcflags=-live=1. Ian -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAOyqgcVVvptvrQ6T92Hb7Z1xw0m-CU%3DRUvvCNEwaugg3EmR0aw%40mail.gmail.com.