内存管理2 | 青训营笔记

73 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天

编译器和静态分析

编译器执行流程: Go 语言编译过程概述 | Go 语言设计与实现

静态分析:

属于编译器后端,不执行程序代码,推导程序行为。分为控制流与数据流

过程内分析与过程间分析:

Intra-procedural analysis: 函数内分析:在函数内进行控制流和数据流的分析 Inter-procedural analysis: 函数间分析:除了函数内的分析,还需要考虑跨函数的数据流和控制流,例如参数传递,函数返回值等

Go编译器优化

函数内联:

内联:编译时将被调用函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定

优点:减少开销,将过程间分析转换为过程内分析

缺点:编译过大

大多数情况内联是正向优化

逃逸分析:

定义:分析代码中指针的动态作用域,即指针在何处可以被访问 大致思路

从对象分配处出发,沿着控制流,观察数据流。若发现指针 p 在当前作用域 s:

作为参数传递给其他函数; 传递给全局变量; 传递给其他的 goroutine; 传递给已逃逸的指针指向的对象; 则指针 p 逃逸出 s,反之则没有逃逸出 s.

优化:未逃逸出当前函数的指针指向的对象可以在栈上分配

对象在栈上分配和回收很快:移动 sp 即可完成内存的分配和回收; 减少在堆上分配对象,降低 GC 负担。

对于微小对象和小对象的内存会首先从mcache和mcentral中获取,这部分要看runtime.malloc代码

微小对象分配

Go中小于16字节的作为微小对象,微小对象会被放入sizeClass为2的span中即16字节,这里并不是说每次微小对象分配都分配一个16字节的空间,而是会把一个16字节的空间按照2、4、8的规则进行字节对齐的形式来存储,比如1字节的char会被分配2字节空间,9字节的数据会被分配2+8=10字节空间。

off := c.tinyoffset
// Align tiny pointer for required (conservative) alignment.
if size&7 == 0 {
off = alignUp(off, 8)
} else if sys.PtrSize == 4 && size == 12 {
// Conservatively align 12-byte objects to 8 bytes on 32-bit
// systems so that objects whose first field is a 64-bit
// value is aligned to 8 bytes and does not cause a fault on
// atomic access. See issue 37262.
// TODO(mknyszek): Remove this workaround if/when issue 36606
// is resolved.
off = alignUp(off, 8)
} else if size&3 == 0 {
off = alignUp(off, 4)
} else if size&1 == 0 {
off = alignUp(off, 2)
}