面试题答案
一键面试Go程序展示内存逃逸场景
package main
import (
"fmt"
)
func allocBigSlice() *[]int {
bigSlice := make([]int, 1000000)
for i := range bigSlice {
bigSlice[i] = i
}
return &bigSlice
}
func main() {
bigSlicePtr := allocBigSlice()
fmt.Println(len(*bigSlicePtr))
}
在上述代码中,allocBigSlice
函数返回一个指向大切片的指针。由于返回的指针会在函数外部使用,导致 bigSlice
无法在栈上分配,从而发生内存逃逸到堆上。
长期监控内存逃逸情况的方案
- 工具:使用
go tool pprof
和runtime/pprof
包。 - 配置:
- 在程序中导入
runtime/pprof
包:
- 在程序中导入
package main
import (
"fmt"
"os"
"runtime/pprof"
)
func allocBigSlice() *[]int {
bigSlice := make([]int, 1000000)
for i := range bigSlice {
bigSlice[i] = i
}
return &bigSlice
}
func main() {
f, err := os.Create("memprofile")
if err != nil {
panic(err)
}
defer f.Close()
err = pprof.WriteHeapProfile(f)
if err != nil {
panic(err)
}
bigSlicePtr := allocBigSlice()
fmt.Println(len(*bigSlicePtr))
}
- 运行程序:`go run main.go`,这会生成一个 `memprofile` 文件。
3. 监控数据的分析思路:
- 使用 go tool pprof
分析内存概要文件:go tool pprof memprofile
。
- 进入 pprof
交互式界面后,可以使用以下命令进行分析:
- top
:显示占用内存最多的函数,通过观察 alloc_objects
和 alloc_space
等指标,可以发现导致内存逃逸的函数,例如 allocBigSlice
函数如果在 top
列表中靠前,就说明它分配了大量内存。
- list <func_name>
:查看特定函数(如 allocBigSlice
)的源代码,并显示每行代码的内存分配情况,进一步定位代码中导致内存逃逸的具体语句。
- web
:生成一个可视化的调用图,以直观展示内存分配的关系和热点,帮助理解哪些函数调用链导致了大量内存逃逸。
为了长期监控,可以定期运行程序生成内存概要文件,然后通过上述方法分析,观察内存逃逸情况是否有变化,例如在代码更新后查看内存逃逸是否增加或减少,及时发现潜在的内存问题。