Go 语言内存管理及编译优化 | 青训营笔记

45 阅读3分钟

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

一、前言

自动内存管理在Go语言运行时runtime的重要组成部分,内存分配和垃圾回收是内存管理系统的两个重要任务,如何高效地进行内存分配和垃圾回收是影响程序效率的重要因素。

二、详细知识点介绍:

垃圾回收

动态内存指的是程序在运行过程中根据需求动态分配的内存

自动内存管理又称垃圾回收(Garbage Collection, GC)指的是由运行时系统管理动态内存,能够避免手动管理内存的麻烦,使得程序员能专注于业务逻辑的实现,并且能够保证内存使用的正确性安全性,例如由手动管理内存可能出现double-free和use-after-free等问题

Mutator指的是业务线程,分配新对象,修改对象之间的关系 Collector指的是GC线程,找到使用中的对象,回收空闲对象的内存空间

评价GC算法的标准:

  • 安全性,最基本的要求
  • 吞吐率,程序实际运行时间和程序执行总时间的比率
  • 暂停时间
  • 内存开销,GC元数据开销

两种常见的GC技术:

  • 追踪垃圾回收 回收对象:指针指向关系不可达的对象
  1. 标记根对象,例如静态变量、全局变量、常量、线程栈等
  2. 从跟对象开始,找到可达对象并将其标记
  3. 清理所有不可达对象,清理策略有几种:
  • 申请一片内存空间,将存活对象复制到该空间,释放原来的内存空间(Copying GC)
  • 将死亡对象的内存标记为可分配(Mark-sweep GC),使用free list管理空闲内存
  • 移动并整理存活对象(Mark-compact GC),原地整理对象,标记可分配空间

清理的优化策略,分代GC(Generational GC)

分代假说:most objects die yong

  1. 记录每个对象的年龄,即经过GC的次数
  2. 将不同年龄的度喜庆分配到heap的不同区域
  3. 针对年轻对象使用Copying GC清理,老年对象使用mark-sweep collection清理
  • 引用计数

时记录每个对象被引用的次数,当引用数>0时才存活 优点:

  • 内存管理的操作被平摊到程序执行过程中
  • 内存管理不涉及太多runtime细节

缺点:

  • 维护引用技术所需开销较大,在多线程时需要通过原子操作保证引用技术操作的原子性和可见性
  • 引用技术会带来额外的内存开销
  • 无法回收环形数据结构(可通过weak reference解决)
  • 回收大内存时可能引发暂停

内存分配

Go的内存分配机制类似于TCMalloc: Thread-Caching Malloc,实现的思想有点类似于CPU中的L1、L2、L3缓存。每个Goroutine独占一定大小的mspan和mcentral,多个Goroutine共享一个大的堆空间。

Go语言编译器及优化

编译器是重要的系统软件,能够识别符合语法和非法的程序,生成正确且高效的代码。编译器可由几个部分组成:

  • 分析部分(front end)
    • 词法分析,生成词素(lexeme)
    • 语法分析,生成语法树
    • 语义分析,收集类型信息,语义检查
    • 中间代码生成,生成intermediate representation(IR)
  • 综合部分 (back end)
    • 代码优化,进行机器无关优化
    • 代码生成,生成目标代码

代码优化:

  • 静态分析:不执行程序代码,推导程序行为,分析程序的性质
    • 控制流分析,生成控制流程图
    • 数据流分析 根据控制流分析和数据流分析的信息优化代码
    • 过程内分析,仅在函数内部进行分析
    • 过程间分析,需要同时考虑函数之间的调用的数据流和控制流,比较复杂

三、引用参考:

字节跳动内部课