Skip to content

Instantly share code, notes, and snippets.

@binderclip
Last active April 9, 2023 22:40
Show Gist options
  • Save binderclip/f2d01b56fa37cb8c18e2f3277a7adafc to your computer and use it in GitHub Desktop.
Save binderclip/f2d01b56fa37cb8c18e2f3277a7adafc to your computer and use it in GitHub Desktop.

golang 结构体内部变量被引用实验

结论

struct 的某个 filed 被引用的时候 struct 本身就不会被垃圾回收器回收。

操作步骤

$ go run memtest.go -cpuprofile cpu.pprof -memprofile mem.pprof
$ go run memtest.go -cpuprofile cpu2.pprof -memprofile mem2.pprof -smallstruct
# 没有 pprof 工具需要安装一下
$ go get -u github.com/google/pprof
$ pprof -http=:8080 mem.pprof
$ pprof -http=:8081 mem2.pprof

结果

big struct 占用 7G+ 内存

image

small struct 占用 3M+ 内存

image

参考

  1. Getting to Go: The Journey of Go's Garbage Collector - The Go Blog
  2. Chris's Wiki :: blog/programming/GoInteriorPointerGC
  3. Does go GC keep whole objects in memory, while interior pointer to one field remains? Can this cause memory leaks? - Stack Overflow
  4. Does go's garbage collector support interior pointers? - Google 网上论坛
  5. Returning pointer on struct member or embedded struct in Go? - Stack Overflow
  6. Go: Should I Use a Pointer instead of a Copy of my Struct?
  7. Go语言101
    1. 内存块 - Go语言101(通俗版白皮书)
    2. 内存布局 - Go语言101(通俗版白皮书)
    3. 一些可能的内存泄漏场景 - Go语言101(通俗版白皮书)
  8. Go 语言原本
    1. 第 4 章 内存管理工程 | Go 语言原本
    2. 第 7 章 内存分配 | Go 语言原本
    3. 第 8 章 垃圾回收 | Go 语言原本
  9. 深入解析Go
    1. 基本类型 · 深入解析Go
    2. 内存管理 · 深入解析Go
  10. Go 语言设计与实现
    1. Go 语言内存分配器的实现原理 | Go 语言设计与实现
    2. Go 语言垃圾收集器的实现原理 | Go 语言设计与实现
    3. Go 语言的栈内存和逃逸分析 | Go 语言设计与实现
  11. 第 83 期对 Go 程序进行可靠的性能测试 · Issue #564 · talk-go/night
package main
import (
"flag"
"fmt"
"log"
"os"
"runtime/pprof"
)
var cpuprofile = flag.String("cpuprofile", "cpu.pprof", "write cpu profile to file")
var memprofile = flag.String("memprofile", "mem.pprof", "write mem profile to file")
var smallstruct = flag.Bool("smallstruct", false, "use small struct for test if set")
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
_ = pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
// do something
doSomething(*smallstruct)
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
_ = pprof.WriteHeapProfile(f)
_ = f.Close()
}
}
const aLotValue = 10000
func doSomething(smallstruct bool) {
s := make([]*int, aLotValue)
for i := 0; i < aLotValue; i++ {
s[i] = getInteriorValue(smallstruct)
}
fmt.Println(s[len(s)-1])
}
const aLotSpace = 200000
func getInteriorValue(smallstruct bool) *int {
type bigStruct struct {
someBigThing [aLotSpace]int
smallThing int
}
b := bigStruct{smallThing: 3}
if !smallstruct {
return &b.smallThing
}
type smallStruct struct {
smallThing int
}
b2 := smallStruct{smallThing: b.smallThing}
return &b2.smallThing
}
// 10000 * 200000 * 4 / 1024 / 1024 / 1024 = 7.450580597
@binderclip
Copy link
Author

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