Reputation: 372
Especially when the variable is generated inside a local scope. How long is its survival time?
For example, given a loop creates 10 dogs and pass the pointers along into a channel, like
for i := 0; i < 10; i++ {
dogAddr := produce(i) // assume we already have: func produce(i int) *Dog
c <- dogAddr // c: channel
}
When the loop ends, will the dogs be released immediately? will they survive only for a magic time to wait for being consumed, and Will they be released after being consumed?
I tested this in a simple code, and results seems to show that the local variables will survive forever.
package main
import (
"fmt"
"time"
)
func main() {
var a int
var c chan *int = make(chan *int, 1000)
var m map[int]*int = make(map[int]*int)
for i := 0; i < 10; i++ { // this is generation-loop
x := i
m[i] = &x
fmt.Println(i, "mapping to: ", &a)
c <- &x
} // the generation-loop breaks here
for i := 0; i < 10; i++ {
fmt.Println(i, "stored pointer: ", m[i]) // we can still call the variables
}
for i := 0; i < 10; i++ {
fmt.Println(i, "stored value: ", *m[i]) // we can still call the variables
p := <-c
fmt.Println(i, "channel value: ", *p) // we can still call the variables
}
time.Sleep(20 * time.Second)
}
I am very confused on why this could happens. Aren't local variables lose their life as long as local blocks get finished? If the way I used is wrong, then what is the correct way to pass local variables to an outer user in Go?
Upvotes: 1
Views: 1292
Reputation: 239462
Go is garbage collected. Resources are freed when there are no more references to them, including references currently held in buffered channels. You don't have to worry about use-after-free, and there is nothing wrong with returning/sending pointers to "local" variables.
Aren't local variables lose their life as long as local blocks get finished?
No, they "lose their life" when the garbage collector finds no further references to their value. Variables that outlive their enclosing scope are automatically allocated on the heap and are safe to use after flow returns from the enclosing scope and its stack memory is lost.
Think of it this way: There is no such thing in Go as a "local" variable that outlives its scope. That is impossible. A variable that lives beyond the scope in which it is declared is by definition not "local" in that sense, it is automatically moved to the heap and lives as long as anything continues to reference it.
Something worth expanding on:
for i := 0; i < 10; i++ { dogAddr := produce(i) // assume we already have: func produce(i int) *Dog c <- dogAddr // c: channel }
When the loop ends, will the dogs be released immediately?
Your confusion seems to stem either from the idea that the dogAddr
variable itself is somehow synonymous with the memory it points to, or from the mistaken idea that the dogAddr
pointer going out of scope would somehow cause the memory it points to to be reclaimed while other things still point to it, which isn't true in any language, garbage collection or not.
The dogAddr
pointer just contains an address. That variable does go out of scope each iteration of the loop, but the the value it holds (the address of a Dog
object on the heap) has already been copied by value into the channel. Yes, dogAddr
is a "local" variable, but that's not really relevant. Its value is not "local", its value is a memory address for some non-local Dog
object allocated down inside process()
.
I tested this in a simple code, and results seems to show that the local variables will survive forever.
No, you've just shown that, as long as you have a reference to a bit of memory, that memory will not be garbage collected.
Upvotes: 5