golang面试题003

victorfengming · · 805 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

## 21, Golang Slice 的底层实现 >切片是基于数组实现的,他的底层是数组, 他自己本身非常小,可以理解为对**底层数组的抽象** >因为基于 数组实现,所以他的底层的 **内存是连续非配的**, 效率非常高, > 还可以通过**索引**获得数据, 可以**迭代以及垃圾回收优化** > >切片本身并不是动态数组或者数组指针.它内部实现的数据结构通过指针引用底层数组, >设定相关属性将数据读写操作限定在指定的区域内.切片本身是一个只读对象,其中工作机制类似数组指针的一种封装. > > 切片对象非常小,是因为 它 是只有3个字段的数据结构: > - 指向底层数组的指针 - 切片的长度 - 切片的容量 ## 22, Golang Slice的扩容机制 ,有什么注意的点 Go 中切片扩容的策略是这样的: ⚫ 首先判断,如果新申请容量大于 2 倍的旧容量,最终容量就是新申请的容 量 ⚫ 否则判断,如果旧切片的长度小于 1024,则最终容量就是旧容量的两倍 ⚫ 否则判断,如果旧切片长度大于等于 1024,则最终容量从旧容量开始循环 增加原来的 1/4, 直到最终容量大于等于新申请的容量 ⚫ 如果最终容量计算值溢出,则最终容量就是新申请容量 ## 23. 扩容前后的Slice是否相同 - 情况一: >原数组还有容量可以扩容(实际容量没有填充完),这种情况下,扩容以后的 数组还是指向原来的数组,对一个切片的操作可能影响多个指针指向相同地址 的 Slice。 > - 情况二: >原来数组的容量已经达到了最大值,再想扩容, Go 默认会先开一片内存区 域,把原来的值拷贝过来,然后再执行 append() 操作。这种情况丝毫不影响 原数组。 要复制一个 Slice,最好使用 Copy 函数 ## 24. Golang 的参数传递,引用类型怎么说 >Go语言中 **所有参数传参都是值传递**,都是一个副本,一个拷贝. 大不了你就传指针呗 > 因为拷 贝的内容有时候是非引用类型(int、string、struct 等这些),这样就在函 数中就无法修改原内容数据;有的是引用类型(指针、map、slice、chan 等 这些),这样就可以修改原内容数据。 >Golang 的引用类型包括 slice、map 和 channel。它们有复杂的内部结构,除 了申请内存外,还需要初始化相关属性。内置函数 new 计算类型大小,为其分 配零值内存,返回指针。而 make 会被编译器翻译成具体的创建函数,由其分 配内存和初始化成员结构,返回对象而非指针 > 所以说能传指针尽量传指针 ## 25、Golang Map 底层实现 >Golang 中 map 的底层实现是一个散列表,因此实现 map 的过程实际上就是实现 散表的过程。 >在这个散列表中,主要出现的结构体有两个,一个叫 hmap(a header for a go map),一个叫 bmap(a bucket for a Go map,通常叫其 bucket) ## 26、Golang Map 如何扩容 **装载因子**:count/2^B **触发条件**: 1. 装填因子是否大于 6.5 2. overflow bucket 是否太多 --- **解决方法**: 1. 双倍扩容:扩容采取了一种称为“渐进式”地方式,原有的 key 并不会一 次性搬迁完毕,每次最多只会搬迁 2 个 bucket 2. 3. 等量扩容:重新排列,极端情况下,重新排列也解决不了,map 成了链表, 性能大大降低,此时哈希种子 hash0 的设置,可以降低此类极端场景的发 生 ## 27、Golang Map 查找 >Go 语言中 map 采用的是哈希查找表,由一个 key 通过哈希函数得到哈希值,64 位系统中就生成一个 64bit 的哈希值,由这个哈希值将 key 对应到不同的桶 (bucket)中,当有多个哈希映射到相同的的桶中时,使用链表解决哈希冲 突。 >key 经过 hash 后共 64 位,根据 hmap 中 B 的值,计算它到底要落在哪个桶 时,桶的数量为 2^B,如 B=5,那么用 64 位最后 5 位表示第几号桶,在用 hash 值的高 8 位确定在 bucket 中的存储位置,当前 bmap 中的 bucket 未找到,则查 询对应的 overflow bucket,对应位置有数据则对比完整的哈希值,确定是否 是要查找的数据。 >如果两个不同的 key 落在的同一个桶上,hash 冲突使用链表法接近,遍历 bucket 中的 key 如果当前处于 map 进行了扩容,处于数据搬移状态,则优先从 oldbuckets 查找 ## 28、介绍一下 Channel >Go 语言中,不要通过共享内存来通信,而要通过通信来实现内存共享。Go 的 CSP(Communicating Sequential Process)并发模型,中文可以叫做通信>顺序进 程,是通过 goroutine 和 channel 来实现的。 >所以 channel 收发遵循先进先出 FIFO,分为有缓存和无缓存,channel 中大致 有 buffer(当缓冲区大小部位 0 时,是个 ring buffer)、sendx 和 recvx 收发 的位置(ring buffer 记录实现)、sendq、recvq 当前 channel 因为缓冲区不足 而阻塞的队列、使用双向链表存储、还有一个 mutex 锁控制并发、其他原属 等。 > > ## 29、Go 语言的 Channel 特性? 1. 给一个 nil channel 发送数据,造成永远阻塞 2. 从一个 nil channel 接收数据,造成永远阻塞 3. 给一个已经关闭的 channel 发送数据,引起 panic 4. 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值 5. 无缓冲的 channel 是同步的,而有缓冲的 channel 是非同步的 6. 关闭一个 nil channel 将会发生 panic ## 30、Channel 的 ring buffer 实现 channel 中使用了 ring buffer(环形缓冲区) 来缓存写入的数据。ring buffer 有很多好处,而且非常适合用来实现 FIFO 式的固定长度队列。 在 channel 中,ring buffer 的实现如下: ![img.png](https://static.studygolang.com/211107/1f5366597f8f0a4b8f599022065d660e.png) hchan 中有两个与 buffer 相关的变量:recvx 和 sendx。其中 sendx 表示 buffer 中可写的 index,recvx 表示 buffer 中可读的 index。 从 recvx 到 sendx 之间的元素,表示已正常存放入 buffer 中的数据。 我们可以直接使用 buf[recvx]来读取到队列的第一个元素,使用 buf[sendx] = x 来将元素放到队尾。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

805 次点击  
加入收藏 微博
上一篇:golang面试题002
下一篇:golang面试题004
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传