Go 如何在 make 或 new 调用中分配内存?
How does Go allocate memory in make or new calls?
当我使用 make 或 new 调用创建新的切片或结构时:
s := make([]int64, 10, 100)
o := new(MyStruct)
Go 通过内存分配系统调用分配了多少内存?它是否预先分配内存,以便后续调用不会触发新的系统调用?
我问这个是因为我需要在我的代码中频繁分配内存。我不确定我是否需要自己实现一个内存分配器,或者我是否可以依赖 Go 来完成这些肮脏的工作。如果 Go 确实预分配内存,我可以自定义分配的块大小吗?
我在Go中写了一些实验代码,运行strace下的代码,但我不明白Go对mmap系统调用做了什么:
mmap(0xc000000000, 65536, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc000000000
munmap(0xc000000000, 65536) = 0
mmap(NULL, 262144, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7efdd1017000
mmap(0xc208000000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc208000000
mmap(0xc207ff0000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc207ff0000
mmap(0xc000000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc000000000
mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7efdd1007000
mmap(NULL, 1439992, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7efdd0ea7000
您可能患有 "premature optimization" 综合症。语言规范没有说明make()
分配的内存将如何分配。
例如,符合规范的 Go 实现可能会对执行 make([]foo, ...)
的函数进行彻底分析并证明:
- 返回的切片值没有超出函数范围。
append()
未在此切片值上调用。
- 它没有传递给任何被调用函数的切片值。
在这种情况下,切片的后备存储可能就在堆栈上分配。
当然,我在夸大其词,但同样,语言规范中并未声明其语义。目前有两个成熟的 Go 实现(一个起源于 Google,被称为 gc
,和 gccgo
)还有更多的实现(llvmgo
似乎有很好的机会不负众望),各有特色
所以请考虑阅读 pprof
和一般的 Go 分析,并分析示例(但真实的)代码。
搜索 the mailing list 词 "profile"、"profiling"、"heap AND profile"、"CPU AND profile" 和 "pprof" 将提供很多关于它的见解给你。
当我使用 make 或 new 调用创建新的切片或结构时:
s := make([]int64, 10, 100)
o := new(MyStruct)
Go 通过内存分配系统调用分配了多少内存?它是否预先分配内存,以便后续调用不会触发新的系统调用?
我问这个是因为我需要在我的代码中频繁分配内存。我不确定我是否需要自己实现一个内存分配器,或者我是否可以依赖 Go 来完成这些肮脏的工作。如果 Go 确实预分配内存,我可以自定义分配的块大小吗?
我在Go中写了一些实验代码,运行strace下的代码,但我不明白Go对mmap系统调用做了什么:
mmap(0xc000000000, 65536, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc000000000
munmap(0xc000000000, 65536) = 0
mmap(NULL, 262144, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7efdd1017000
mmap(0xc208000000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc208000000
mmap(0xc207ff0000, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc207ff0000
mmap(0xc000000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc000000000
mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7efdd1007000
mmap(NULL, 1439992, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7efdd0ea7000
您可能患有 "premature optimization" 综合症。语言规范没有说明make()
分配的内存将如何分配。
例如,符合规范的 Go 实现可能会对执行 make([]foo, ...)
的函数进行彻底分析并证明:
- 返回的切片值没有超出函数范围。
append()
未在此切片值上调用。- 它没有传递给任何被调用函数的切片值。
在这种情况下,切片的后备存储可能就在堆栈上分配。
当然,我在夸大其词,但同样,语言规范中并未声明其语义。目前有两个成熟的 Go 实现(一个起源于 Google,被称为 gc
,和 gccgo
)还有更多的实现(llvmgo
似乎有很好的机会不负众望),各有特色
所以请考虑阅读 pprof
和一般的 Go 分析,并分析示例(但真实的)代码。
搜索 the mailing list 词 "profile"、"profiling"、"heap AND profile"、"CPU AND profile" 和 "pprof" 将提供很多关于它的见解给你。