Go面试精萃(应付go语言层面仅需一篇文章:持续更新)
目录
[TOC]
channel
- 对未定义的channel(也就是nil)进行读写都会被阻塞
- 对已关闭的channel
进行读,会得到类型的零值(int就是0,string就是空字符串)
进行写,会发生panic
进行重复关闭,会发生panic - channel底层实现原理(通过通信来共享内存)
基础:
底层:发送方和接收方都是goroutine,通常与select结合来管理多个goroutine之间的通信 ch := make(chan int) //创建,得到一个hchan结构的指针 data <- ch //读 ch <- 1 //写 close(ch) //关闭
由环形链表和双向链表以及锁实现- 环形链表做buf,用来写入的数据,作为缓冲,有两个指针用来指向写入位置(sendx)和读取位置(recvx)。
- 双向链表是recvq和sendq,分别存放待写入的G(goroutine)和待读取的G,当缓冲buf写满了,那么要往channel写数据的G会在sendq中排队,阻塞着,把M让出来给其他G,直到有G来读取数据时被唤醒。同理recvq也是一样,当没有数据读了,那么读数据的G也在recvq中阻塞着。
- 锁Lock,保证G能按顺序进入recvq和sendq队列,先进先出。
- 优化特性:当读数据的G1被阻塞时,此时有写数据G2来写数据,此时不是将channel锁住,数据先写入buf,而是直接从G2复制数据到G1,从而减少了内存copy。
map
- 对nil的map读值,得到零值
- 对nil的map添加值,会发生panic
- 对nil的map删除值,不会报错
- map底层实现原理
基础:
底层:go的map是非线程安全的,也就是多个协程取写map可能会异常中断报错,不是并发安全。sync.map是线程安全的。
采用hmap结构,count记录元素个数,记录B(数量)个buckets是一个bmap结构的数组,数据存储在bmap中,每个存储8个kv,一旦满了就会溢出指针overflow指向下一个bmap。
读取:读取时计算key的hash,通过最后B位找到对应的bmap,通过前8位找到bmap中对应的值。
扩容:oldbuckets指针用于指向扩容前的buckets数组- map读取是无序的,原因是map写入是无序的,且还有扩容机制(倍数扩容,等量扩容)
slice
切片的底层是数组,数组是值传递(深拷贝),slice(切片)是引用传递(浅拷贝)
nil切片指向地址为0
底层:
ptr指向数组的指针,len为长度,cap为容量
扩容机制:
当容量小于1024,扩容后为原来的2倍
当容量不小于1024,扩容后为原来的1.25倍
本作品采用《CC 协议》,转载必须注明作者和本文链接
GitHub地址:github.com/bllon