go 语言内存管理详解 | 青训营笔记

78 阅读2分钟

go 语言内存管理详解 | 青训营笔记

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

自动内存管理

这部分与 JVM 中 GC 的相关知识比较类似。

在处理垃圾回收的问题时,首先当然是识别哪些是“垃圾”,主要有两种方式

  • 首先标记根对象(静态变量、全局变量、常量、线程栈等),然后找到可达对象(求指针指向关系的传递闭包),然后清理所有的不可达对象。
  • 引用计数:每个对象都有一个与之关联的引用数目,当引用数大于0,则表示该对象存活。

可以把所有的对象分为年轻代和老年代,年轻代可以使用 copying collection 处理,而老年代可以使用 mark-sweep collection 处理。

go 内存管理优化

go 内存分配的目标是为对象在 heap 上分配内存,一般的做法是提前为内存分块,向 OS 申请一大块内存后,将其分为较大块记作 mspan,然后再把mspan划分为特定大小的块,用于对象分配。

因为 go 语言的每一次内存分配都要经过一个很长的过程,对于 go 内存管理优化可以不用每一次直接进行分配,而是将多个小对象的分配合并成一次大对象的分配,而这种操作可能会导致内存被延迟释放,可以仿照 copying GC 的方式,当大对象的总大小超过一定阈值就复制到另外分配的大对象中。

编译器与静态分析

静态分析指的是不执行程序代码,推导分析程序的性质。

go 编译器优化

函数内联,在C++中有类似的操作,就是将被调用函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定,将过程间分析转换为过程内分析。

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

  • 作为参数传递给其他函数
  • 传递给全局变量
  • 传递给其他的 goroutine
  • 传递给已逃逸的指针指向的对象

则指针 p 指向的对象逃逸出 s,反之则没有逃逸出 s。如果发现存在未逃逸对象可以在栈上分配,可以降低 GC 的压力。