Go 源代码/底层原理 · 2023年5月1日 0

GO底层原理【】

1. GO语言的GMP模型是什么,它是怎么调度的,随着版本迭代,GMP都做了哪些优化。

GMP是在Go语言运行时进行调度的,它可以确保在多个线程之间安全的读写共享内存。GMP通过让每个Goroutine获得足够的处理器时间来实现调度。

0.x 单线程调度:程序中只能存在一个活跃线程,由 G-M 模型组成;
流程简单:获取调度器的全局锁、保存栈寄存器和程序计数器、获取下一个需要运行的go并解锁调度器、修改全局线程m上要执行的go
1.0 多线程调度:允许运行多线程的程序;
需要解决:
调度器和锁是全局资源,所有的调度状态都是中心化存储的,锁竞争问题严重;
线程需要经常互相传递可运行的 Goroutine,引入了大量的延迟;
每个线程都需要处理内存缓存,导致大量的内存占用并影响数据局部性;
系统调用频繁阻塞和解除阻塞正在运行的线程,增加了额外开销;
1.1 任务窃取调度器: 引入了处理器 P,构成了目前的 G-M-P 模型;
在处理器 P 的基础上实现了基于工作窃取的调度器;
当前处理器本地的运行队列中不包含 Goroutine 时,会触发工作窃取,从其它的处理器的队列中随机获取一些 Goroutine
需要解决:
在某些情况下,Goroutine 不会让出线程,进而造成饥饿问题;
时间过长的垃圾回收(Stop-the-worldSTW)会导致程序长时间无法工作;
1.2~至今 抢占式调度器:
1.2 基于协作的抢占式调度:
1. 编译器会在调用函数前插入morestack
2. Go 语言运行时会在垃圾回收暂停程序、系统监控发现 Goroutine 运行超过 10ms 时发出抢占请求 StackPreempt
3. 当发生函数调用时,可能会执行编译器插入的 morestack, 它调用newstack 会检查 Goroutinestackguard0字段是否为StackPreempt
4. 如果 stackguard0StackPreempt,就会触发抢占让出当前线程;
需要优化:增加了运行时的复杂度, 紧密循环不能被抢占
1.14基于信号的抢占式调度:
(提案)非均匀内存访问调度器:原理就是通过拆分全局资源,让各个处理器能够就近获取,减少锁竞争并增加数据的局部性。

随着版本GMP都做了哪些优化:
M是可以复用的,不需要反复创建与销毁,当没有可执行的Goroutine时候就处于自旋状态,等待唤醒
Work StealingHand Off策略保证了M的高效利用
内存分配状态(mcache)位于PG可以跨M调度,不再存在跨M调度局部性差的问题
M从关联的P中获取G,不需要使用锁,是lock free

2. 调度机制有哪些?他们的原理是什么?

GMP采用了轮转调度算法,将协程分为多个调度队列,每个队列有自己的优先级,每个队列里的协程被调度的顺序是循环的,这样可以保证每个协程都有平均的运行时间。GMP会根据协程的执行情况,动态地进行调度,降低了调度的开销,提高了程序的效率。随着Go语言版本的不断更新,GMP也不断进行优化,提高了调度的效率和稳定性。

每一个M都需要与一个P绑定,P拥有本地可运行G队列,M是执行G的单元,M获取可运行G流程是先从P的本地队列获取,若未获取到,则从其他P偷取过来(即work steal),若其他的P也没有则从全局G队列获取,若都未获取到,则M将处于自旋状态,并不会销毁。
当执行G时候,发生通道阻塞等用户级别阻塞时候,此时M不会阻塞,M会继续寻找其他可运行的G,当阻塞的G恢复之后,重新进入P的队列等待执行,若G进行系统调用时候,会阻塞M,此时P会和M解绑(即hand off),并寻找新的空闲的M。若没有空闲的就会创建一个新的M。

3. GMP的work stealing 和 hand off 机制。

Work stealing 是指当一个线程(goroutine)无法继续运行时,它将它剩余的工作分配给其他线程(goroutine)完成。这种分配方式有助于确保系统中的所有线程(goroutine)得到充分利用,并最大限度地利用系统的资源。

Hand off 是指当一个线程(goroutine)开始执行 I/O 操作时,它将当前的任务分配给其他线程(goroutine),以等待 I/O 操作的完成。这种方式有助于提高系统的吞吐量,因为它允许其他线程(goroutine)继续运行。

4. GMP调度时存在那些阻塞操作?

1. I/O 操作:等待 I/O 操作完成时线程会被阻塞。
2. 网络操作:等待网络请求完成时线程会被阻塞。
3. Channel 操作:等待 Channel 操作完成时线程会被阻塞。
4. 等待锁:等待对某个锁的获取时线程会被阻塞。

5. GO语言的垃圾回收是怎么实现的,有哪些流程。随着版本迭代都有哪些优化?

>原始标记清除法:
1. 标记追踪:从根集合(寄存器、执行栈、全局变量)开始遍历对象图,标记遇到的每个对象;
2. 清扫回收:检查堆中每一个对象,将所有未标记的对象当做垃圾进行回收。
原始标记清除算法带来的长时间STW引入三色标记算法。
为了解决悬挂指针屏障机制: 插入屏障, 删除屏障。

堆压缩,这减少了碎片,并允许GC有效地回收更多内存。
增量GC,它通过与程序同时运行GC来减少停止世界的暂停时间。
更好地预测堆增长,这减少了不必要的GC循环数。
更好地利用多个CPU内核,这使GC能够更快地完成任务。

6. 三色标记法的实现原理和具体步骤。对比其他语言,还有那些垃圾回收算法。主要是对比Java 和 php

三色标记:
白色对象(可能死亡):未被回收器访问到的对象。在回收开始阶段,所有对象均为白色,当回收结束后,白色对象均不可达。
灰色对象(波面):已被回收器访问到的对象,但回收器需要对其中的一个或多个指针进行扫描,因为他们可能还指向白色对象。
黑色对象(确定存活):已被回收器访问到的对象,其中所有字段都已被扫描,黑色对象中任何一个指针都不可能直接指向白色对象。
当垃圾回收开始时,只有白色对象。随着标记过程开始进行时,灰色对象开始出现(着色),这时候波面便开始扩大。当一个对象的所有子节点均完成扫描时,会被着色为黑色。当整个堆遍历完成时,只剩下黑色和白色对象,这时的黑色对象为可达对象,即存活;而白色对象为不可达对象,即死亡。这个过程可以视为以灰色对象为波面。

1. 引用计数算法:为每个对象维护一个引用计数,当引用计数为0时,对象将被回收。
2. 分代回收算法:对内存空间进行划分,每一代代表不同的存活时间长度,经过多次回收后,对象的存活时间可能会变长,并从年轻代迁移到年老代。
3. 标记-整理算法:标记所有被引用的对象,然后整理所有未被引用的对象,并将它们移动到内存空间的一端。

7. 插入,删除,混合写屏障分别是什么,解决什么问题?

以下两个条件同时满足时会破坏垃圾回收器的正确性,不应出现对象的丢失,也不应错误的回收还不需要回收的对象。
条件 1: 赋值器修改对象图,导致某一黑色对象引用白色对象;
条件 2: 从灰色对象出发,到达白色对象的、未经访问过的路径被赋值器破坏。

1. 插入屏障:增量更新屏障(incremental update)核心思想是把赋值器对已存活的对象集合的插入行为通知给回收器,进而产生可能需要额外(重新)扫描的对象。 如果某一对象的引用被插入到已经被标记为黑色的对象中,这类屏障会保守地将其作为非白色存活对象, 以满足强三色不变性。
解决什么问题:满足强三色不变性,避免满足条件 1 的出现,性能优势:它不需要对指针进行任何处理,因为指针的读操作通常比写操作高出一个或更多数量级。
缺点:在一次回收过程中可能会产生一部分被染黑的垃圾对象,只有在下一个回收过程中才会被回收;
2. 删除屏障:基于起始快照的屏障(snapshot-at-the-beginning)。 其思想是当赋值器从灰色或白色对象中删除白色指针时,通过写屏障将这一行为通知给并发执行的回收器。 这一过程很像是在操纵对象图之前对图进行了一次快照。如果一个指针位于波面之前,则删除屏障会保守地将目标对象标记为非白色存活对象,进而避免条件 2 来满足弱三色不变性。
解决什么问题:避免条件 2 来满足弱三色不变性,不需要标记结束阶段的重新扫描, 结束时候能够准确的回收所有需要回收的白色对象。
缺点: 删除屏障会拦截写操作,进而导致波面的退后,产生冗余的扫描
3. 混合写屏障:对正在被覆盖的对象进行着色,且如果当前栈未扫描完成, 则同样对指针进行着色。
解决什么问题:将强三色不变性进行了弱化,从而消除了对栈的重新扫描这一硬性要求,使得在未来实现全面并发 GC 成为可能。
缺点:但缺点也非常明显,因为着色成本是双倍的,而且编译器需要插入的代码也成倍增加, 随之带来的结果就是编译后的二进制文件大小也进一步增加。

8. GO语言什么时候会触发垃圾回收,如何调优?

垃圾回收总共有三种触发方式:申请内存,定时触发,主动触发。
Go语言垃圾回收的触发时机:
1. 堆内存使用量达到一定阈值:堆内存使用量达到一定阈值时,Go语言的垃圾回收器就会启动。
2. 在GC周期结束后:Go语言的垃圾回收器会在一个固定的周期内进行垃圾回收。
3. 程序需要申请新的内存:当程序需要申请新的内存时,垃圾回收器会启动以确保有足够的内存可以分配。
Go语言的垃圾回收器的调优,以下是一些常见的方法:
1. 使用GODEBUG环境变量:通过设置GODEBUG环境变量,可以控制垃圾回收器的行为,如调整垃圾回收周期等。
2. 调整GOGC环境变量:通过调整GOGC环境变量,可以控制垃圾回收器的工作强度,从而影响垃圾回收的效率。
3. 使用内存池:对于一些内存频繁申请和释放的场景,可以使用内存池来

打赏 赞(0) 分享'
分享到...
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

文章目录