前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【建议收藏】整理Golang面试第二篇干货13问

【建议收藏】整理Golang面试第二篇干货13问

作者头像
公众号-利志分享
发布2022-06-13 15:19:27
1.6K0
发布2022-06-13 15:19:27
举报
文章被收录于专栏:利志分享
问:数组和切片的相同点和区别

相同点:

  1. 只能存储一组相同类型的数据结构
  2. 都是通过下标来访问,并且有容量长度,长度通过len获取,容量通过cap获取

区别:

  1. 数组是定长,访问和复制不能超过数组定义的长度,否则就会下标越界,切片长度和容量可以自动扩充
  2. 数组是值类型,切片是引用类型,每个切片都引用了一个底层数组,切片本身不能存储任何数据,都是这底层数组存储数据,所以修改切片的时候修改的是底层数组中的数据。切片一旦扩容,指向一个新的底层数组内存地址也就随之改变。
问:for range 的时候它的地址会发生变化么?

答:在for a,b := range c 遍历中, a 和 b 在内存中只会存在一份,即之后每次循环时遍历到的数据都是以值覆盖的方式赋给 a 和 b,a,b 的内存地址始终不变。由于有这个特性,for循环里面如果开协程,不要直接把a或者b的地址传给协程。

问:Go多返回值怎么实现的

答:Go传参和返回值是通过FP+offset实现,并且存储在调用函数的栈帧中。FP栈底寄存器,指向一个函数栈的顶部;PC程序计数器,指向下一条执行指令;SB指向静态数据的基指针,全局符号;SP 栈顶寄存器。

问:map相关的一些问题

问:map 使用注意的点,并发安全? 并发不安全,如果出现两个以上的协程写同一个map会报错,使用读写读写锁解决。 问:map 循环是有序的还是无序的? 无序的 问:map 中删除一个 key,它的内存会释放么? 通过delete删除map的key,执行gc不会,内存没有被释放,如果通过map=nil,内存才会释放 问:怎么处理对 map 进行并发访问? 通过加读写锁RWMutex,也可以使用sync.Map 问:nil map 和空 map 有何不同? nil map是未初始化的map,空map是长度为空

问:哪些方式安全读写共享变量

答:

  1. 将共享变量的读写放到一个 goroutine 中,其它 goroutine 通过 channel 进行读写操作。
  2. 可以用个数为 1 的信号量(semaphore)实现互斥
  3. 通过Mutex 锁实现
问:Go 如何实现原子操作

答:原子操作就是不可中断的操作,外界是看不到原子操作的中间状态,要么看到原子操作已经完成,要么看到原子操作已经结束。在某个值的原子操作执行的过程中,CPU绝对不会再去执行其他针对该值的操作,那么其他操作也是原子操作。 Go语言的标准库代码包sync/atomic提供了原子的读取(Load为前缀的函数)或写入(Store为前缀的函数)某个值 原子操作与互斥锁的区别

  1. 互斥锁是一种数据结构,用来让一个线程执行程序的关键部分,完成互斥的多个操作。
  2. 原子操作是针对某个值的单个互斥操作。
问:Mutex 是悲观锁还是乐观锁?悲观锁、乐观锁是什么?

答:Mutex是悲观锁 悲观锁:当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制【Pessimistic Concurrency Control,缩写“PCC”,又名“悲观锁”】。 乐观锁:乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。

问:Mutex 有几种模式?

sync.Mutex 有两种模式,正常模式和饥饿模式。 正常模式:等待的goroutines按照FIFO(先进先出)顺序排队,但是goroutine被唤醒之后并不能立即得到mutex锁,它需要与新到达的goroutine争夺mutex锁。因为新到达的goroutine已经在CPU上运行了,所以被唤醒的goroutine很大概率是争夺mutex锁是失败 的。出现这样的情况时候,被唤醒goroutine需要排队在队列的前面。如果被唤醒的goroutine有超过1ms没有获取到mutex锁,那么它就会变为饥饿模式。在饥饿模式中,mutex锁直接从解锁的goroutine交给队列前面的goroutine。新达到的goroutine也不会去争夺mutex锁(即使没有锁,也不能去自旋),而是到等待队列尾部排队。正常模式有更好的性能,因为goroutine可以连续多次获得mutex锁。 饥饿模式:锁的所有权将从unlock的gorutine直接交给交给等待队列中的第一个。新来的goroutine将不会尝试去获得锁,即使锁看起来是unlock状态,也不会去尝试自旋操作,而是放在等待队列的尾部。如果有一个等待的goroutine获取到mutex锁了,如果它满足下条件中的任意一个, mutex将会切换回去正常模式:是等待队列中的最后一个goroutine和它的等待时间不超过1ms。饥饿模式能阻止尾部延迟的现象,对于预防队列尾部goroutine一致无法获取mutex锁的问题。

问:goroutine 的自旋占用资源如何解决?

答:自旋锁是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断地判断是否能够被成功获取,直到获取到锁才会退出循环。 自旋的条件如下:

  1. 还没自旋超过 4 次,
  2. 多核处理器,
  3. GOMAXPROCS > 1,
  4. p 上本地 goroutine 队列为空。

mutex 会让当前的 goroutine 去空转 CPU,在空转完后再次调用 CAS 方法去尝试性的占有锁资源,直到不满足自旋条件,则最终会加入到等待队列里。

问:谈谈内存泄漏,什么情况下内存会泄漏?怎么定位排查内存泄漏问题?

答:go中的内存泄漏一般都是goroutine泄漏,就是goroutine没有被关闭,或者没有添加超时控制,让goroutine一只处于阻塞状态,不能被GC。

内存泄露有下面一些情况

  1. 如果goroutine在执行时被阻塞而无法退出,就会导致goroutine的内存泄漏,一个goroutine的最低栈大小为2KB,在高并发的场景下,对内存的消耗也是非常恐怖的。
  2. 互斥锁未释放或者造成死锁会造成内存泄漏
  3. time.Ticker是每隔指定的时间就会向通道内写数据。作为循环触发器,必须调用stop方法才会停止,从而被GC掉,否则会一直占用内存空间。
  4. 字符串的截取引发临时性的内存泄漏
代码语言:javascript
复制
func main() {
 var str0 = "12345678901234567890"
 str1 := str0[:10]
}
  1. 切片截取引起子切片内存泄漏
代码语言:javascript
复制
func main() {
   var s0 = []int{0,1,2,3,4,5,6,7,8,9}
   s1 := s0[:3]
}
  1. 函数数组传参引发内存泄漏【如果我们在函数传参的时候用到了数组传参,且这个数组够大(我们假设数组大小为100万,64位机上消耗的内存约为800w字节,即8MB内存),或者该函数短时间内被调用N次,那么可想而知,会消耗大量内存,对性能产生极大的影响,如果短时间内分配大量内存,而又来不及GC,那么就会产生临时性的内存泄漏,对于高并发场景相当可怕。】

排查方式:一般通过pprof是Go的性能分析工具,在程序运行过程中,可以记录程序的运行信息,可以是CPU使用情况、内存使用情况、goroutine运行情况等,当需要性能调优或者定位Bug时候,这些记录的信息是相当重要。

问:请简述 Go 是如何分配内存的?

Go程序启动的时候申请一大块内存,并且划分spans,bitmap,areana区域;arena区域按照页划分成一个个小块,span管理一个或者多个页,mcentral管理多个span供现场申请使用;mcache作为线程私有资源,来源于mcentral。

问:Channel 分配在栈上还是堆上?

Channel被设计用来实现协程间通信的组件,其作用域和生命周期不可能仅限于某个函数内部,所以golang直接将其分配在堆上。

问:介绍一下大对象小对象,为什么小对象多了会造成 gc 压力?

小于等于32k的对象就是小对象,其它都是大对象。一般小对象通过 mspan 分配内存;大对象则直接由 mheap 分配内存。通常小对象过多会导致GC三色法消耗过多的CPU。优化思路是,减少对象分配。

参考文献:

书籍《go专家编程》

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 利志分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问:数组和切片的相同点和区别
  • 问:for range 的时候它的地址会发生变化么?
  • 问:Go多返回值怎么实现的
  • 问:map相关的一些问题
  • 问:哪些方式安全读写共享变量
  • 问:Go 如何实现原子操作
  • 问:Mutex 是悲观锁还是乐观锁?悲观锁、乐观锁是什么?
  • 问:Mutex 有几种模式?
  • 问:goroutine 的自旋占用资源如何解决?
  • 问:谈谈内存泄漏,什么情况下内存会泄漏?怎么定位排查内存泄漏问题?
  • 问:请简述 Go 是如何分配内存的?
  • 问:Channel 分配在栈上还是堆上?
  • 问:介绍一下大对象小对象,为什么小对象多了会造成 gc 压力?
    • 参考文献:
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档