Golang面试前一夜准备
题号 | 题目 |
6 | 协程和线程和进程的区别 |
7 | Golang的内存模型中为什么小对象多了会造成GC压力 |
8 | Go中数据竞争问题怎么解决 |
9 | 什么是channel,为什么它可以做到线程安全 |
10 | Golang垃圾回收算法 |
6. 协程和线程和进程的区别
- 进程
进程是程序的一次执行过程,是程序在执行过程中的分配和管理资源的基本单位,每个进程都有自己的地址空间,进程是系统进行资源分配和调度的一个独立单位。
每个进程都有自己的独立内存空间,不同进程通过IPC(Inter-Process Communication)进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
- 线程
线程是进程的一个实体,线程是内核态,而且是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
- 协程
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。
协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
7. Golang的内存模型中为什么小对象多了会造成GC压力
通常小对象过多会导致GC三色法消耗过多的CPU。优化思路是,减少对象分配(内存分配请看往期文章).
8. Go中数据竞争问题怎么解决
Data Race问题可以使用互斥锁sync.Mutex, 或者也可以通过CAS无锁并发解决.
其中使用同步访问共享数据或者CAS无锁并发是处理数据竞争的一种有效的方法.
golang在1.1之后引入了竞争检测机制,可以使用 go run -race 或者 go build -race来进行静态检测。
其在内部的实现是,开启多个协程执行同一个命令, 并且记录下每个变量的状态.
竞争检测器基于C/C++的ThreadSanitizer运行时库,该库在Google内部代码基地和Chromium找到许多错误。这个技术在2012年九月集成到Go中,从那时开始,它已经在标准库中检测到42个竞争条件。现在,它已经是我们持续构建过程的一部分,当竞争条件出现时,它会继续捕捉到这些错误。
竞争检测器已经完全集成到Go工具链中,仅仅添加-race标志到命令行就使用了检测器。
$ go test -race mypkg // 测试包 $ go run -race mysrc.go // 编译和运行程序 $ go build -race mycmd // 构建程序 $ go install -race mypkg // 安装程序
要想解决数据竞争的问题可以使用互斥锁sync.Mutex,解决数据竞争(Data race),也可以使用管道解决,使用管道的效率要比互斥锁高.
9. 什么是channel,为什么它可以做到线程安全
Channel是Go中的一个核心类型,可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication),Channel也可以理解是一个先进先出的队列,通过管道进行通信。
Golang的Channel,发送一个数据到Channel和从Channel接收一个数据都是原子性的。
Go的设计思想就是, 不要通过共享内存来通信,而是通过通信来共享内存,前者就是传统的加锁,后者就是Channel。
也就是说,设计Channel的主要目的就是在多任务间传递数据的,本身就是安全的。
10. Golang垃圾回收算法
Golang 1.5后,采取的是“非分代的、非移动的、并发的、三色的”标记清除垃圾回收算法。
golang 中的 gc 基本上是标记清除的过程:
golang 的垃圾回收是基于标记清扫算法,这种算法需要进行 STW(stop the world),这个过程就会导致程序是卡顿的,频繁的 GC 会严重影响程序性能.
golang 在此基础上进行了改进,通过三色标记清扫法与写屏障来减少 STW 的时间.
gc的过程一共分为四个阶段:
- 栈扫描(开始时STW),所有对象最开始都是白色.
- 从 root开始找到所有可达对象(所有可以找到的对象),标记为灰色,放入待处理队列。
- 遍历灰色对象队列,将其引用对象标记为灰色放入待处理队列,自身标记为黑色。
- 清除(并发) 循环步骤3直到灰色队列为空为止,此时所有引用对象都被标记为黑色,所有不可达的对象依然为白色,白色的就是需要进行回收的对象。三色标记法相对于普通标记清扫,减少了 STW 时间. 这主要得益于标记过程是 "on-the-fly" 的,在标记过程中是不需要 STW 的,它与程序是并发执行的,这就大大缩短了STW的时间.
Golang gc 优化的核心就是尽量使得 STW(Stop The World) 的时间越来越短。
更加详细的GC我会在后面单独章节拿出来讲解(源码+图解),为了应付面试这些就足够了。
写屏障:
“当标记和程序是并发执行的,这就会造成一个问题. 在标记过程中,有新的引用产生,可能会导致误清扫.
” “清扫开始前,标记为黑色的对象引用了一个新申请的对象,它肯定是白色的,而黑色对象不会被再次扫描,那么这个白色对象无法被扫描变成灰色、黑色,它就会最终被清扫,而实际它不应该被清扫.
” “这就需要用到屏障技术,golang采用了写屏障,其作用就是为了避免这类误清扫问题. 写屏障即在内存写操作前,维护一个约束,从而确保清扫开始前,黑色的对象不能引用白色对象.
”
GC的触发条件:
Go中对 GC 的触发时机存在两种形式:
- 主动触发(手动触发),通过调用runtime.GC 来触发GC,此调用阻塞式地等待当前GC运行完毕.
- 被动触发,分为两种方式:a. 使用系统监控,当超过两分钟没有产生任何GC时,强制触发 GC. b. 使用步调(Pacing)算法,其核心思想是控制内存增长的比例,当前内存分配达到一定比例则触发.