Go语言自动内存管理 | 青训营笔记

57 阅读3分钟

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

自动内存管理

概念

自动内存管理:由程序语言的运行时系统回收动态内存

  • 避免手动内存管理,专注于实现业务逻辑
  • 保证内存使用的正确性和安全性

相关名词

  • Mutator:业务线程,分配新对象,修改对象指向关系
  • Collector:GC线程,找到存活对象,回收死亡对象的内存空间
  • Serial GC:只有一个collector
  • Parallel GC: 支持多个collectors同时回收的GC算法
  • Concurrent GC: mutator(s) 和 collector(s)可以同时执行
    • Collectors 必须感知对象指向关系的改变

image.png

Serail GC是多个Mutator并行执行,当GC时,需要暂停后一个collector去回收,而Parallel GC则是在暂停时多个collectors同时回收。

Concurrent GC 是当需要GC时,并不需要暂停,可以一边运行collector一边运行mutator,当collector结束后,继续Mutator执行。但是进行垃圾回收时有可能出现新的需要标记的存活对象,所以collector(s)需要去感知对象指向关系的改变。

  • 评价 GC 算法
    • 安全性:不能回收存活的对象 基本要求
    • 吞吐率:1 - GC时间程序运行总时间\frac{GC时间}{程序运行总时间} 花在GC上的时间
    • 暂停时间 业务是否感知
    • 内存开销 GC元数据开销
  • 追踪垃圾回收
  • 引用计数

追踪垃圾回收

  • 对象被回收的条件:指针指向关系不可达的对象
  • 标记根对象
  • 标记:找到可达对象
  • 清理:所有不可达对象
    • 将存活对象复制到另外的内存空间(Copying GC)
    • 将死亡对象的内存标记为"可分配"(Mark-sweep GC)
    • 移动并整理存活对象(Mark-compact GC)
  • 根据对象的生命周期,使用不同的标记和清理策略

分代GC

  • 分代假说:most objects die young

  • Intuition:很多对象在分配出来后很快就不再使用了

  • 每个对象都有年龄:经历过GC的次数

  • 目的:对年轻和老年的对象对象,制定不同的GC策略,降低整体内存管理的开销

  • 不同年龄的对象处于Heap的不同区域

  • 年轻代

    • 常规的对象分配
    • 由于存活对象很少,可以采用 copying collection
    • GC吞吐率很高
  • 老年代

    • 对象趋向于一直活着,反复复制开销较大
    • 可以采用 mark-sweep collection

引用计数

  • 每个对象都有一个与之关联的引用数目
  • 对象存活的条件:当且仅当引用数大于0
  • 优点
    • 内存管理的操作被平摊到程序执行过程中
    • 内存管理不需要了解 runtime 的实现细节: C++智能指针
  • 缺点
    • 维护引用计数的开销较大:通过原子操作保证对引用计数的原子性和可见性
    • 无法回收环形数据结构 ————— weak reference
    • 内存开销:每个对象都引入的额外内存空间存储引用数目
    • 回收内存时依然可能引发暂停

个人总结

  • 自动内存管理可以让我们更专注于编码本身,而不需要过于去关注对象是否回收了
  • 可能还会出现一些更好的GC算法,一些GC算法也一直不断改进,需要我们保持关注