整理了一些面试题,关于 go 语言面试会考的东西,顺便整理自己的知识体系。
一、语言机制
select
是随机的还是顺序的?
先说答案,
select
会随机选择一个可用通道做收发操作。为什么呢?在源码中每一个select
对应一个hselect
结构,每个hselect
结构下面都有个scase
的数组记录每个case
, 在scase
中记录着ch chan
的结构也就是channel
的结构pollorder
将元素从新排列,scase
就被乱序了。
2.Go 语言局部变量分配在栈还是堆?
视逃逸分析结果而定,Go 语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析,当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆。
参考资料 : GO 语言变量逃逸分析
3.Go 语言逃逸分析有那集中场景?
- 指针逃逸:典型的逃逸 case, 函数返回局部变量的指针。
- 间接赋值:对某个引用类对象中的引用类成员进行赋值。Go 语言中的引用类数据类型有 func, interface, slice, map, chan, *Type (指针)。
- 栈空间不足逃逸:当对象大小超过的栈帧大小时(详见 go 内存分配), 变量对象发生逃逸被分配到堆上。
- 闭包引用逃逸:外部函数引用内部函数变量,发生逃逸,分配到堆上。
- 动态类型逃逸:当对象不确定大小或者被作为不确定大小的参数时发生逃逸。
- 切片或 map 赋值:在给切片或者 map 赋值对象指针(与对象共享内存地址时), 对象会逃逸到堆上。但赋值对象值或者返回对象值切片是不会发生逃逸的。
不要盲目使用变量的指针作为函数参数,虽然它会减少复制操作。但其实当参数为变量自身的时候,复制是在栈上完成的操作,开销远比变量逃逸后动态地在堆上分配内存少的多。
4. 简述一下你对 Go 垃圾回收机制的理解?
v1.1 STW
v1.3 Mark STW, Sweep 并行
v1.5 三色标记法
v1.8 hybrid write barrier (混合写屏障:优化 STW)
参考资料 : Go 实时 GC—— 三色算法理论与实践 , Golang 垃圾回收剖析
5. 简述一下 golang 的协程调度原理?
M (machine): 关联了一个内核线程。
P (processor): 代表了 M 所需的上下文环境,也是处理用户级代码逻辑的处理器。
G (goroutine): 调度系统的最基本单位 goroutine, 存储了 goroutine 的执行 stack 信息、goroutine 状态以及 goroutine 的任务函数等。
参考资料 : Go 并发原理
6. 简单介绍下 golang 中 make 和 new 的区别?
new(T)
是为一个 T 类型的新值分配空间,并将此空间初始化为 T 的零值,并返回这块内存空间的地址,也就是 T 类型的指针 *T, 该指针指向 T 类型值占用的那块内存.make(T)
返回的是初始化之后的 T (引用类型本身), 且只能用于 slice, map, channel 三种类型. make (T, args) 返回初始化之后 T 类型的值,且此新值并不是 T 类型的零值,也不是 T 类型的指针 *T, 而是 T 类型值经过初始化之后的引用.
参考资料 : Go 语言中 new 和 make 的区别
7. 介绍下你平时都是怎么调试 golang 的 bug 以及性能问题的?
- panic 调用栈
- pprof
- 火焰图 (配合压测)
- 使用
go run -race
或者go build -race
来进行竞争检测- 查看系统 磁盘 IO / 网络 IO / 内存占用 / CPU 占用 (配合压测)
8. 如何获取 go 程序运行时的协程数量,gc 时间,对象数,堆栈信息?
调用接口
runtime.ReadMemStats
可以获取以上所有信息,注意:调用此接口会触发 STW (Stop The World)
9. 介绍下 golang 的 runtime 机制?
runtime 负责管理任务调度,垃圾收集,以及运行环境。go 提供了一些高级的功能,如 goroutine, channel, 以及 Garbage collection。这些高级功能需要一个 runtime 的支持. runtime 和用户编译后的代码被 linker 静态链接起来,形成一个可执行文件。这个文件从操作系统角度来说是一个 user space 的独立的可执行文件。 从运行的角度来说,这个文件由 2 部分组成,一部分是用户的代码,另一部分就是 runtime。runtime 通过接口函数调用来管理 goroutine, channel 及其他一些高级的功能。从用户代码发起的调用操作系统 API 的调用都会被 runtime 拦截并处理。
Go runtime 的一个重要的组成部分是 goroutine scheduler。他负责追踪,调度每个 goroutine 运行,实际上是从应用程序的 process 所属的 thread pool 中分配一个 thread 来执行这个 goroutine。因此,和 java 虚拟机中的 Java thread 和 OS thread 映射概念类似,每个 goroutine 只有分配到一个 OS thread 才能运行。
10.goroutine 池是否像其他语言中的线程池一样有意义?
视情况而定,调度程序中的状态成本可以忽略不计,但是 goroutine 保持的状态重新创建的成本可能很高。后一点可用作将 goroutine 保留在池中的理由。但是,另一方面,在大多数情况下,将执行类似任务的 goroutine 的资源组池化(而不是 goroutine 本身)更容易。
参考资料 : Does a goroutine pool make sense like thread pools in other languages?
Be the first person to leave a comment!