想学习数据库面试知识点的朋友,关注狗蛋儿,后续相关内容更新。大家都是苦逼程序员,如果能帮到朋友们,我自然非常乐意。
channel为nil或关闭时,读写会怎么样?
- channel 为 nil 时读:永久阻塞。
- channel 为 nil 时写:永久阻塞。
- channel 关闭时写:触发 panic。
- channel 关闭时读:返回nil。
channel 什么时候会阻塞?
- channel为nil时,读写都会阻塞。
- channel 无数据,读会阻塞。
- channel 缓存buffer占满,写会阻塞。
channel 怎么实现无阻塞?
利用select的default语句。
init函数init函数在一个GO文件的个数?
一个文件可以有0个、1个或多个。
多个init函数的执行顺序?
同文件中从上到下顺序执行。如有引用文件,先执行引用文件 init 函数。
init和main函数可以共存吗?
可同时存在,init函数执行完后再执行 mian 函数。
可以含有返回值和参数吗?
不能,main函数也不能。
引用类型有哪些?指针、channel、接口、切片、map、函数。
序列化不能序列化的类型有哪些?
complex、channel、function
结构体存在complex、channel、function,序列化时就会产生错误?
不一定,如果这类成员变量是小写字母开头,其将不会进行序列化,就不会报错。
selectselect只能处理IO操作吗?
是的,它只能处理IO操作,每个case后必须是IO操作。
select需添加条件判断?
select后不跟条件判断。不同于switch,switch可以跟也可以不跟
select的执行顺序?是否阻塞?
case随机执行。
- 如果没有case收到消息,且没有default语句,便会阻塞直到收到channel消息。
- 如果没有case收到消息,有default语句,便执行default语句后跳出select,不会阻塞。
- switch 后可以有判断条件,也可以没有。这和Java和C++有所不同。
- 结构体作为接受者,当为接口类型变量赋值时,无论结构体初始化变量,还是结构体指针初始化变量都可以编译通过。
- 结构体指针作为接受者,当为接口类型变量赋值时,只能结构体指针初始化变量才能编译通过。
type Wish interface {
GetGirlFriend(number int)
}
type Aladdin struct {}
func (Aladdin) GetGirlFriend(number int) {
...
}
var wish Wish = new(Aladdin) // 正确
var wish Wish = Aladdin{} // 正确
var wish Wish = &Aladdin{} // 正确,上述是通过结构体作为接受者,所以初始化变量时,使用指针也没问题
cap适用哪些类型?适用map吗,表示什么?
cap使用array、slice、channel。
- array:查看数组元素个数,cap和len相同。
- slice:查看最大容量。len查看元素个数。
- channel:查看buffer最大容量。len查看元素个数。
- map:不适用,可以使用len()获取大小
result := i++
GO可以对指针进行哪些操作?
- 取地址&
- 取指针执行的数据*
- 不能对指针进行自增自减操作。
不能,只能使用var
var str string
var str = ""
stri := "" //错误
什么是 fallthrough
GO switch语法中使用,当没有fallthrough语句,case执行完后会默认break跳出switch语句;当含有fallthrough语句,则不会跳出并继续进入下个case执行。
通过range修改slice/map元素的值是否合理?不合理,range是值传递,修改没有效果。而在java里“更像是引用传递”,修改有效果。
下面代码能否遍历输出?不能,闭包引用了非参数的值,都是引用传递。所以下面代码中,协程使用的是元素的引用,而这个元素值在随着range的遍历在不断变化,协程到底使用的哪个值便无法预估了。
str := []string{"I","am","Sergey"}
for _,v := range str{
go func() {
fmt.Println(v)
}()
}
time.Sleep(1 * time.Second)
// 程序打印出“Sergey”、“Sergey”、“Sergey”。
// v的值在随着range发生变化(类似迭代器),而传递的又是引用,当第一个协程开始打印的时候,v已经变换为指向了最后一个元素。
协程panic后的过程是怎么样?
当前调用栈函数终止运行,调用defer函数(函数中存在多个defer函数按后入先出执行),然后按照函数调用栈向上传递,如果没有遇到recover,最终到达main函数终止,然后程序崩溃,程序中其它协程无法执行defer函数,也无法recover该panic。
makemake channel/slice/map都必须指定长度?
只有slice必须指定长度。其它两种未指定长度默认长度为0。
make、new、var 区别?
- make只能对slice、channel、map操作,会进行初始化,比如初始化长度、容量和为每个成员赋初值,并返回初始化的值。
- new可对所有类型操作,用来分配空间,并不会进行初始化,返回指针。实际上new和var没有任何区别,都不会初始化,返回的是变量的零值,只是new得到变量地址,var得到变量值。
空切片能否append?
可以。
扩容机制?
容量未到1024时,按照翻倍扩容,超过1024时,每次扩容四分之一左右。
切片和数组的区别
- 数组必须指定长度,切片长度不定。
- 在作为形参时,数组为值传递,切片效果像是引用传递(实际为值传递)。
包含那几个重要结构体?
hmap
- B:决定桶的个数,也就是bmap的个数,个数为2^B。
- count:size of map。
- buckets:指针数组,每个指针指向一个 bmap。
bmap
桶,存放key/value的结构体。
- topbits:数组,长度为8位。存放 hash 前8位。
- keys/values:分别对应两个数组,长度为8。存放键值对。
- overflow:桶中存放的键值对超过8个时,新加入该桶的键值对放到overflow中。这和java不同,java如果超过8个,由链表变成红黑树。
添加流程?
continue
获取流程?
判断 key 是否存在的方式和上述一样。找到了则取出,否则返回value类型的零值。
map未初始化时可以进行增删查操作吗?
可以进行删、查。不能进行增。
channel 缓存的实现?环形缓冲区,recvx表示读取的位置,sendx表示写入的位置。超出环形缓冲区的元素,使用双向链表存储。
协程并发什么是GMP?
- G:goroutine,协程。
- M:machine,内核线程的封装。
- P:processor,管理G和M之间调度等关联关系。每个P需要绑定一个M,由于P将G交给M执行。
GMP调度流程?
- P有一个G的局部队列,保存着等待M执行的G。当P局部队列已满,便将G放入全局队列。
- M绑定的P的局部队列为空时,M便会去全局队列中取G来执行。如果全局队列为空,M回去其它P局部队列中偷取G来执行。
- G 因系统调用阻塞时,会阻塞M,此时P会和M解绑即hand off,P去寻中空闲的M,如果没有,创建一个新的M。
- G 因IO阻塞,不会阻塞M,M继续运行其它的G,阻塞的G恢复后重新进入P局部队列。
五IO模型(非面试热点)
- 阻塞:数据未就绪,原线程阻塞挂起让出cpu,等待数据就绪后,内核唤醒线程,线程进入等待队列,等待cpu调度执行。
- 非阻塞:数据未就绪,函数直接返回错误码,原线程继续执行不会暂停挂起。
- IO 复用:select\epoll,也是阻塞的,只是循环监听着众多文件描述符。
- 信号驱动 IO:数据未就绪,原线程继续执行不会暂停挂起,数据就绪后内核会发送SIGIO信号,由处理SIGIO信号的函数进行后续操作。
- 异步IO:数据未就绪,原线程继续执行不会暂停挂起,等数据就绪后,内核调用回调函数(io 的相关系统调用,可以设置回调函数)。
所以很明显是没有什么异步非阻塞的,真实的io模型就只有这五种。
什么是sysmon?有什么功能?
类似Linux的定时线程,go语言本身也有一个监护线程,由于它本身是个线程,便不需要绑定P。
- 释放闲置5分钟的内存
- 超过2分钟没有垃圾回收,强制垃圾回收。
- 将长时间未处理的netpoll添加到全局队列。
- 向长时间运行的G发出抢占调度。
- 收回系统调用长时间阻塞的G。
根对象
垃圾回收之前,首先了解go中根对象指的什么:全局对象、栈中局部变量。
当然在java中根对象复杂一些,包含:栈中对象、方法区静态对象、方法区常量对象、native 对象。
简述三色标记法流程
三色标记实际是通过标记对象为三种不同状态,来对对象进行回收。
- 所有对象都标记为白色。
- 每次GC从根节点遍历对象,把遍历到的对象放入灰色区。
- 遍历灰色对象,将灰色对象引用到的白色对象,加入灰色区,将原灰色对象本身放入黑色区。
- 重复第三步,直到没有灰色对象。
- 清除白色对象,因为此时白色对象不属于根对象,也没被其它对象引用,属于垃圾可以回收。
什么STW?
stop the world
一定需要STW?–屏障机制
STW
插入屏障
垃圾回收期间,将新建引用的对象标记为灰色。
避免,白对象建立黑对象引用后,白对象仍被误删。
因为栈和寄存器不能hook,所以栈没有插入屏障机制,那么栈的垃圾回收仍需要STW。
删除屏障
垃圾回收期间,引用被删除的对象标记为灰色。
避免,白对象和灰对象引用被删除,但立刻新建立了和黑对象的引用关系后,白色对象仍被误删。
但是这样做精度降低了,也就是在这一轮遍历回收中,即便对象所有引用都解除了,它也只会被放到下一轮回收才被清理。
注意,插入和删除屏障都是写屏障哈,读屏障没涉及
混合屏障
插入和删除屏障的结合,同时赋值器也变成了黑色赋值器,见下文。无需STW,但也存在精度问题,它的流程如下:
- GC开始将栈上的对象全部扫描并标记为黑色。
- GC期间,任何在栈上创建的新对象,均为黑色。
- 被删除的对象标记为灰色。
- 被添加的对象标记为灰色。