这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。介绍了进行功能优化的必要性和go言语中的功能优化办法,常见的GC算法以及go的三色符号法。

前语——功能优化

什么是功能优化?

​ 进步软件体系处理才能,减少不必要的消耗,充沛开掘计算机算力。

为什么要功能优化?

​ 进步用户体验

​ 资源高效运用:下降成本,进步功率——很小的优化乘以海量机器会显著进步功能和成本节约

功能优化的层面:事务层优化和言语运转时层面优化
事务代码
SDK
基础库
言语运转时:GC、调度器等
OS

主动内存办理

1.概念

中英文对照:

  • Auto memory management: 主动内存办理

  • Grabage collction: 废物收回

  • Mutator: 事务线程

  • Collector: GC 线程

  • Concurrent GC: 并发 GC

  • Parallel GC: 并行 GC

  • Tracing garbage collection: 追寻废物收回

    • Copying GC: 仿制目标 GC
    • Mark-sweep GC: 符号-收拾 GC
    • Mark-compact GC: 符号-压缩 GC
  • Reference counting: 引证计数

  • Generational GC: 分代 GC

    • Young generation: 年轻代
    • Old generation: 老时代

2.内存走漏(Memory leak)

内存走漏是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成体系内存的浪费,导致程序运转速度减慢甚至体系崩溃等严重后果。

内存走漏会导致内存溢出,无法继续申请内存空间,内存已经占满了。

3.主动内存办理(Auto memory management)

动态内存:程序在运转时依据需求动态分配的内存

主动内存办理(废物收回):由程序言语的运转时体系办理动态内存

​ 可有效避免手动内存办理,使咱们在编写代码时专注于完成事务逻辑

​ 保证内存运用的正确性安全性:duble-free problem,use-after-free problem

三个任务:为新目标分配空间

​ 找到存活目标

​ 收回逝世目标的内存空间

4.Go中主动内存办理

Mutator:事务线程,分配新目标,修改目标指向联系

Collector:GC 线程,找到存活目标,收回逝世目标的内存空间

Serial GC:只有一个collector

Parallel GC:支撑多个collectors一起收回的GC算法

Concurrent GC: mutator(s)和collector(s)能够一起履行

5.两种常用的GC算法

追寻废物收回(Tracing garbage collection)

引证计数(Reference counting)

2.追寻废物收回(Tracing garbage collection)

2.1算法思想

目标被收回的条件:指针指向联系不可达的目标

判别一个目标是否可达,由于一旦这个目标不可达就能够立刻被 GC 收回了。那么咱们怎么判别一个目标是否可达呢?

第一步,符号根目标,即找出一切的全局变量和当时函数栈里的变量,常量等,符号为可达;

第二步,从已经符号的数据开始,进一步符号它们可访问的变量,以此类推,专业术语叫传递闭包。

收拾一切不可达目标:三种方式,依据目标的生命周期,运用不同的符号和收拾战略

​ 1.符号-仿制——将存活目标仿制到另外的内存空间(Copying GC)

​ 2.符号-铲除——将逝世目标的内存符号为可分配(Mark-sweep GC):运用free-list办理闲暇内存

​ 3.符号-收拾——移动并收拾存活目标(Mark-compact GC):原地收拾目标

2.2分代GC(Generational GC)

追寻式废物搜集算法在少量废物收回的时分功率十分高效,特别是仿制收回算法。可是长命目标的存在会影响到收收回回的功率,这个时分就经过分区,使长命的数据都堆积在一边,这样对年轻的数据运用仿制收回算法就能够大大进步功率。

分代假说(Generational hypothesis):most objects die young

  • 弱分代假说:大多数目标都是朝生夕灭的
  • 强分代假说:越长命的目标越不容易逝世

设计原则:搜集器将堆划分出不同区域,然后将收回目标依据年纪分配到不同的区域之中。目标年纪指的是其经历过GC的次数。

不同年纪的目标应有不同的GC战略:

​ 年轻代(Young generation):常规的目标分配;由于存活目标很少,能够采用符号-仿制战略;GC吞吐率很高

​ 老时代(Old generation):目标趋向于一向活着,重复仿制开支较大;能够采用符号-铲除战略

2.3三色符号法(tricolor mark-and-sweep algorithm)

Golang运用的废物收回算法是三色符号法,是传统符号-铲除的一个改进,是一个并发的GC算法。

原理如下:

第一步,创立白、灰、黑三个调集;

第二步,将一切目标放入白色调集中;

第三步,从根节点开始遍历一切目标,把遍历到的目标从白色调集放入灰色调集(补白:这里放入灰色调集的都是根节点的目标);

第四步,遍历灰色调集,将灰色目标引证的目标(补白:这里指的是灰色目标引证到的一切目标,包括灰色节点直接引证的那些目标)从白色调集放入灰色调集,然后将剖析过的灰色目标放入黑色调集,直到灰色中无任何目标;

第五步,经过写屏障(write-barrier)检测目标有变化,重复以上操作(补白:由于 mark 和用户程序是并行的,所以在上一步履行的时分或许会有新的目标分配,写屏障是为了解决这个问题引进的);

第六步,搜集一切白色目标(废物)。

缺陷:或许程序中的废物发生的速度会大于废物搜集的速度,这样会导致程序中的废物越来越多无法被搜集掉。

3.引证计数(Reference counting)

每个目标都有一个与之关联的引证数目。

目标的存活条件:当且仅当引证数大于0

优点:内存办理的操作被平摊到程序履行过程中

​ 内存办理不需要了解runtime的完成细节

缺陷:维护引证计数的开支较大:经过原子操作保证对引证计数操作的原子性和可见性

​ 无法收回环形数据结构——weak reference

​ 内存开支:每个目标都引进的额定内存空间存储引证数目

​ 收回内存时依然或许引发暂停

4.Go内存办理

4.1分块

目标:为目标在heap上分配内存

提前将内存分块:调用体系调用mmap(),向OS申请一大块内存;将其划分红大块,称作mspan,再将大块分红特定巨细的小块,用于目标分配,依据目标的巨细,选择最合适的块回来。小块有两种类型,

​ noscan mspan:分配不含指针的目标,GC不需要扫描

​ scan mspan:分配含指针的目标,GC需要扫描

4.2缓存

GMP模型中,每个p包括一个mcache用于快速分配,用于为绑定于p上的G分配目标;mcache办理一组mspan

4.3优化计划(字节):Balanced GC

每个G都绑定一大块内存(1KB),称作 goroutine allocation buffer(GAB)

GAB用于noscan类型的小目标分配:<128 B

GAB关于Go内存办理来说是一个目标。

实质:将多个小目标的分配合并成一次大目标的分配

4.4逃逸剖析

剖析代码中指针的动态效果域:指针在何处能够被访问

大致思路:从目标分配处动身,沿着控制流,调查目标的数据流,若发现指针 p 在当时效果域 s 出现一下情况:

​ 作为参数传递给其他函数

​ 传递给全局变量

​ 传递给其他的goroutine

​ 传递给已逃逸的指针指向的目标

​ 则指针p指向的目标逃逸出 s ,反之则没有逃逸出 s 。

未逃逸的目标在栈上分配。目标在栈上的分配和收回很快:移动sp;能够减少在heap上的分配,下降GC担负。