浅谈go 内存管理设计
常见的很多设计,其出发点都在于替换简单粗暴的系统调用或者os默认行为。
比如db存储引擎会选择实现不同的页管理,一方面需要支持事务一类的高级语义,另一方面,模型总是建立在特定的统计规律上的,更明确的场景可以选择更特殊的优化策略,从而优于宽泛的(默认的)策略。
对于go的内存管理,其设计主要体现了:
1.池化资源,有利于复用,减少系统调用(直接获取资源)的开销
2.内存管理本身的复杂性,这不算特点哈哈哈哈,元数据的层次结构,逻辑与物理的边界与联系
简单介绍:
mheap:分两类:全局(central)与局部(线程内cache),体现了分配与回收的边界
mheap分割:mcentral,负责一个类型(有两个,一个包含指针,一个不包含指针不需要gc),两个链表表示可用与已用(元素span)
span:内存管理的基本单位,逻辑上管理对象个体(位图),管理页,每个页分为多个块
area:物理,粒度:页
gc:本质是自动的对象生命周期管理
内存块标记:allocBits(物理,分配的块),gcmarkBits(逻辑,存活的对象),gc完成,allocBits=gcmarkBits
三色标记:dfs,标记root出发不可达点(回收,白),标记过程中分子树遍历完成(dfs结束,黑)和未完成的(灰),(吐槽下,总觉得三色归于一个逻辑范畴有点怪,刷题有修改原图的技巧)
stw:
容器修改与遍历并发问题,gc需要的是一个快照,并发修改可能破坏连通性
优化方案:写屏障,保证新分配的内存立即标记,想到一个奇怪的观点,事务开始后检测到其他事务对数据的修改,则不对相关数据做任何改动。
写屏障本身保证的是内存上的一致性,逻辑上而言,简单理解成只有一个内存数据(本人希望把讨论的边界限制于体系结构之外,因为菜)
辅助gc:均摊gc开销,分配内存的goroutine执行部分回收工作
逃逸分析:偏向于语言设计层次,1.对象生命周期(动态)延长至定义域(静态)外。2.对象分配开销考虑,栈空间局限性
返回局部变量的指针
栈空间不足
动态类型
闭包引用对象
TODO:
手动管理的内存泄漏与指针悬空引用问题也许对gc的思路有帮助?
内存屏障的细节
linux堆栈内存分配与限制
浙公网安备 33010602011771号