Go内存管理 | 青训营笔记

71 阅读3分钟

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

Go内存管理 | 青训营笔记

自动内存管理

首先,管理的是动态内存(根据需求动态分配的内存)
自动内存管理(垃圾回收, garbage collection,简称gc):由程序语言的运行时系统管理动态内存,避免手动管理,保证内存使用的正确性和安全性。

三个任务

  • 为新对象分配空间
  • 找到存活对象
  • 回收死亡对象

image.png

image.png

追踪垃圾回收

  • 标记根对象:静态变量、全局变量、常量等
  • 标记可达对象:从根对象出发,找指向关系的传递
  • 清理不可达对象:
    • 存活对象复制到另外内存空间
    • 死亡对象标记为可分配
    • 移动并整理存活对象(类似于copy只不过没有额外空间只能原地复制) 对象被回收条件:指针指向关系不可达对象

分代GC(generational gc)

分代假说:很多对象分配完很快就不再使用了
每个对象都赋予年龄(经历过gc的次数),对年轻和老年的对象制定不同的gc策略,不同年龄放在堆的不同区域

  • 年轻代:存活对象少,用复制做清理
  • 老年代:一直活着,反复复制开销大,用mark-sweep collection

引用计数

  • 每一个对象都有一个与之关联的引用数目
  • 对象存活条件,当且仅当应用数大于0

优点

  • 可以在程序执行过程中进行引用计数
  • 不需要了解runtime细节
    缺点
  • 维护开销大:因为对象可能被多个协程引用,所以需要进行原子操作
  • 内存开销大
  • 无法回收引用环

Go内存分配

目标:为对象在堆上分配内存
做法:将内存分块,根据对象大小找一个最合适的块返回

image.png

缓存

image.png

image.png

编译器和静态分析

image.png

静态分析:不执行代码,推导程序行为,分析程序性质
控制流:程序执行流程
数据流:数据在控制流上传递

过程内分析:仅在函数内部进行分析
过程间分析:考虑函数调用时的函数传递和返回值的数据流和控制流。比较复杂,需要同时考虑数据流和控制流。

Go编译器优化

image.png

函数内联

内联:把函数体副本直接替换到调用位置上。
优点:减少了传递参数,保存寄存器等开销。同时把过程间分析转化为过程内分析。大多数情况是正向优化。
缺点: 函数体变大

Go语言特性导致内联受到较多限制,且内敛策略较多。

逃逸分析

image.png 函数内联使更多对象不逃逸
优化:未逃逸对象在栈上分配,减少堆上分配,降低gc负担