大家直接在评论区发布答案就可以哦~
课后作业- 重点内容 Review
- 从业务层和语言运行时层进行优化分别有什么特点?
- 从软件工程的角度出发,为了保证语言SDK的可维护性和可拓展性,在进行运行时优化时需要注意什么?
- 自动内存管理技术从大类上分为哪两种,每一种技术的特点以及优缺点有哪些?
- 什么是分代假说?分代 GC 的初衷是为了解决什么样的问题?
- Go 是如何管理和组织内存的?
- 为什么采用 bump-pointer 的方式分配内存会很快?
- 为什么我们需要在编译器优化中进行静态代码分析?
- 函数内联是什么,这项优化的优缺点是什么?
-
什么是逃逸分析?逃逸分析是如何提升代码性能的?
< 作业提交截止时间:5月12日 10:00前 >
正确答案:
- 业务层的优化需要针对具体问题具体分析;而语言运行时层的优化针对的是更通用的问题,需要在多方面进行 tradeoff。两者都需要自动化性能分析工具的支持。
- 因为语言 SDK 会被大量广泛使用,保证接口的稳定性和一致性至关重要。对已有接口的改动,需要保证兼容性;对新增的接口,需要提供明确的文档说明。对于功能的改动,最好通过选项进行隔离,保证新增改动在不打开的情况下不影响原本的功能。
-
主要分为追踪垃圾回收和引用计数:
a. 追踪垃圾回收
1️⃣ 回收内存的条件:回收不可达的对象; 2️⃣ 回收时,首先会扫描 GC roots,例如栈上的对象、全局变量等; 3️⃣ 从 GC roots 出发,沿着指针指向,追踪所有的可达对象; 4️⃣ 追踪完成后,回收所有不可达对象的内存。b. 引用计数
1️⃣ 对象有一个与之关联的引用数目。当且仅当引用数大于 0 时对象存活; 2️⃣ 当对象的引用数目为 0 时,内存可以被回收; 3️⃣ 优点:内存管理的开销被平摊到程序运行中,且内存管理不需要了解 runtime 的实现细节; 4️⃣ 缺点:维护引用计数的开销较大;额外的内存开销存储引用计数;无法回收带有环的数据结构;回收大数据结构时依然会造成程序暂停。
- 大量的对象会很快死去。分代 GC 的思想是:针对不同生命周期的对象采取不同策略的内存管理机制。
-
Go 使用 TCMalloc 风格的内存管理方式。
a. TC 是 thread caching 的简写。每个线程都绑定 cache 方便线程快速分配内存;
b. 内存被划分为特定大小的块。根据对象是否包含指针,将内存块分为 scan 和 noscan 两种;
c. 根据内存分配请求的大小,选择合适的内存块返回,完成一次内存分配操作;
d. 回收的内存不会立刻还给操作系统,而是在 Go 内部缓存起来,方便下次分配。
- 每个线程都持有用于对象分配的 buffer,因此指针碰撞方式的内存分配无需加锁或使用 CAS 操作;对象分配的操作非常简单。
- 通过静态分析,我们可以获取更多关于程序的非平凡特性 (non-trivial properties);这些关于程序的知识可以指导编译器优化。例如通过逃逸分析得知对象并未逃逸出当前函数,因此对象可以在栈上分配,避免频繁在堆上分配对象,降低 GC 的压力。
-
函数内联:将被调用函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定。
a. 优点
① 消除函数调用; ② 由于没有了函数调用,过程间分析转化为过程内分析;b. 缺点
① 函数体变大; ② 编译生成的 Go 镜像变大。
-
逃逸分析:分析代码中指针的动态作用域,即指针在何处可以被访问。
通过逃逸分析得知对象并未逃逸出当前函数,因此对象可以在栈上分配,避免频繁在堆上分配对象,降低 GC 的压力。