我正在学习 Go 的内存管理和垃圾收集,并且在尝试在指针指向的变量超出范围后取消引用指针时遇到了一些意外行为。
在Go中,我知道垃圾收集(GC)是自动的,并且指针有时可以引用不再有效的内存。我预计取消引用指向范围外变量的指针会引发错误,但似乎 Go 这样做时确实会引发错误
这是一个演示该问题的最小示例:
package main
import (
"fmt"
"runtime"
)
func main() {
var p *int
{
x := 42
p = &x // p points to x
fmt.Println(*p) // Prints 42
}
// Force garbage collection (for testing)
runtime.GC()
// Dereferencing p here should cause an error (since x is out of scope)
fmt.Println(*p) // This does NOT throw any error
}
我的期望:
实际发生了什么:
简单的答案是:如果需要,
x
会转义到堆。
仅仅因为变量超出范围并不意味着指向它的所有指针都悬空。 Go 编译器会检测是否存在指向变量的指针,如果它认为对该变量的引用在作用域结束后仍存在,则会将该变量移动到堆中。
也就是说,在上面的情况下,
x
甚至有可能不会逃逸到堆,因为编译器可以检测到指向 x
的指针在作用域之后仍然存在,但在函数返回之后则不然。所以在这种情况下 x
仍然可以存在于堆栈中。