Go语言内存管理

现代高级编程语言管理内存分为两种:自动手动, Python,Go使用自动内存管理系统(内存分配器垃圾收集器)代为分配和回收内存。C,C++等编程语言使用手动管理内存的方式.需要主动申请和释放内存.

Go 内存分配

Go语言里,从内存的分配到不再使用后内存的回收等等内存管理工作都是由Go在底层完成的.Go内存管理的设计宗旨是在并发环境中快速运行,并与垃圾回收器集成在一起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

type smallStruct struct {
a, b int64
c, d float64
}

// 禁止Go对该函数进行内联
//go:noinline
func smallAllocation() *smallStruct {
return &smallStruct{}
}

func main() {
smallAllocation()
}

函数上面的注释//go:noinline将禁止Go对该函数进行内联,这样main函数就会使用smallAllocation函数返回的指针变量

内联是一种手动或编译器优化,用于将简短函数的调用替换为函数体本身。这么做的原因使它可以消除函数调用本身的开销,也使得编译器能更高效地执行其他的优化策略

通过逃逸分析go tool compile -m main.go 确认&smallStruct{}被分配到堆上

1
2
3
$ go tool compile -m main.go
/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:13:6: can inline main
/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:10:9: &smallStruct{} escapes to heap

如果没有//go:noinline,通过分析go tool compile -m main.go, &smallStruct将不会被分配到堆上

1
2
3
4
5
/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:8:6: can inline smallAllocation
/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:12:6: can inline main
/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:13:17: inlining call to smallAllocation
/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:9:9: &smallStruct{} escapes to heap
/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:13:17: &smallStruct{} does not escape

go tool compile -S main.go 显示该程序的汇编代码

1
2
3
0x0018 00024 (/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:10)     MOVD    $type:main.smallStruct(SB), R0
0x0020 00032 (/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:10) PCDATA $1, $0
0x0020 00032 (/Users/chyiyaqing/chyi/github.com/gotutorial/demo/main.go:10) CALL runtime.newobject(SB)

Go有两种内存分配策略,一种适用于程序例小内存块的申请,另一种适用于大内存快的申请(32KB)

小于32KB内存的分配策略

当程序里发生32kb一下的小块内存申请时,Go会从mcache本地缓存给程序分配内存,这个本地缓存mcache持有一系列大小为32kb的内存快,这样的内存块叫作mspan.

大于32KB内存块的分配策略

Go没办法使用工作线程的本地缓存mcache和全局中心缓存mcentral上管理超过32KB的内存分配,所以对于那些超过32KB的内存申请,会直接从堆上(mheap)上分配对应的数量内存页

1
2
3
4
5
6
总结关于Go内存分配管理的策略如下:

1. Go在程序启动时,会向操作系统申请一大块内存,由`mheap`结构全局管理
2. Go内存管理的基本单元是mspan, 每种mspan可以分配特定大小的object
3. mcache,mcentral,mheap是Go内存管理的三大组件,mcache管理线程在本地缓存的mspan, mcentral管理全局的mspan供所有线程使用,mheap管理Go的所有动态分配内存
4. 一般小对象通过mspan分配内存,大对象则直接由mheap分配内存

栈内存管理

应用程序的内存会分成堆区(Heap)和栈区(Stack)两部分.程序在运行期间可以主动从堆区(heap)申请内存空间,这些内存由内存分配器分配并由垃圾收集器负责回收.栈区(stack)的内存编译器自动进行分配和释放,栈区中存储着函数的参数以及局部变量,他们会随着函数的创建而创建,函数的返回而销毁.


Go语言内存管理
https://blog.chyidl.com/2024/02/02/Go语言内存管理/
作者
Yaqing Chyi
发布于
2024年2月2日
许可协议