Memory Manager of GoRuntime
Contents
前言
Go
内存管理是runtime
比较重要的一部分,Go
内存管理算法来至于TCMalloc
,非常类似。tcmalloc
已经发展好长一段时间了,是非常高效的一种内存管理算法,下面简单聊一下tcmalloc
。
虚拟内存
在现代系统中,保护模式下,进程看到的内存地址都是虚拟地址
,可以通过页表进行转换,转换到实际的主存RAW或者磁盘中。
在go中,对象的地址在编译的时候就确定,来验证一下.
|
|
Run:
1 2 3 4 5 6 |
└─[$]> ./virtual_addr 5 at 0xc0000160c0 └─[$]> ./virtual_addr 27 at 0xc0000160c0 └─[$]> ./virtual_addr 60 at 0xc0000160c0 |
多次运行,对象i的地址都是一致的
TCMalloc
tcmalloc
采用分层的设计,其内存对象被划分为*Small*、Large 2个等级,每个等级的对象占用内存各不相同,每个线程都有自己的本地缓存(谷歌出品比属精品)。
结构:
内存分类
Small
小于等于32Kb
的对象
小对象被映射成170
种规格的大小的小对象,每个的规格不一定相同,但满足2^N Bytes
,如:
|
|
分配
Large
大于32Kb
的对象
一共有256中,[1,255]
类型每一项分别占用n*4k
,rest
为特殊项。
分配
- [1,255]
|
|
内存申请可以使用
(using sbrk, mmap, or by mapping in portions of /dev/mem
- 256
和前面的步骤类似.
1)大对象的分配由Central heap负责;2)Requested size is rounded up to number of pages(4kB)
be round up to 向上取整
Span
Manages memory in units called Span
- Runs of contiguous memory pages
- Metadata is kept separated from the allocation arena
分配流程
直接从Central Heap
分配,以页
来适配,比如,对象的大小大于一页,则分配2页的空间。
层次
Thread Cache
CentralCache
PageHeap
Go Memory
Interview
|
|
注: 关闭内敛编译,否则经过编译器优化后
|
|
编译:go build -gcflags "-m -m" alloc.go
|
|
关键点:
- &i escapes to heap
逃逸到堆
- move to heap: i
i 迁移到堆(作为指针返回)
查看一下反汇编的情况:
|
|
runtime.newobject申请对象
层级
和 tcmalloc
不同,go memory分为3个level,分别为tiny, small, large
- Tiny
小于等于16字节(no pointer)
- small
小于等于32字节大于16字节
- Large
大于32字节
Go 分配器
go的垃圾回收是并行
的,是不是全部步骤都是并行
?
- 检索所有对象(并行)
- 标记哪些对象是active的(并行)
- 交换未active的对象(stop the world)
Small Allocator
At source-code: sizeclasses.go
66
种规格小对象,具体意思如class1
:
- byets/object
每个对象的代销不超过8字节
- bytes/span
跨度为8192 kB = 8 kB
- objects
可分配的对象,计算过程:(8 kB / 8B) = 1k = 1024
即每个span
管理1024
个这种类型的对象
源码中有几个关键的常量:
|
|
|
|
分配流程
Tiny Allocator
内存释放
- 运行时周期性释放内存到操作系统
- Releases spans that were swept more than 5 minutes ago
- In Linux, uses the madvise(2) syscall
|
|
span
span有三种状态:1)mSpanInUse,2)mSpanManual,3)mSpanFree
转态转移:
- 在GC期间,span可能从free转为in-use或者mannual
- 在sweeping阶段(gcphase== _GCoff),span从in-use转为free(由于sweeping的结果) 或者 从manual转为free(由于栈释放)
- 在GC(gcphase != GCoff)期间,span必须不能mannual或者in-use转为free状态。因为并发gc可能会读到一个指针并且查找到这个指针指到的span,span的状态必须是单向的
实践
Trace
|
|
Understanding Running Go Programs - GopherConSG 2018