Golang 面试笔录

golang初级面试

new和make之间的区别?

例子:
    var temp []int
    // 相当于给 temp 变量分配了结构类型,没有分配底层数组
    // 所以 data = nil、存储个数为0,容量也是0
    
    var temp []int = make([]int, 4, 8)
    temp = append(temp, 111, 222)
    // 相当于为 temp 变量分配结构类型, 存储个数为 4, 容量也是 8, 和分配底层数组
    // 分配一块容纳 8 个元素的内存,还会初始化为默认 0 
    // 通过 append 进行赋值
    
    temp := new([]int)
    // 相当于给 temp 分配结构类型,但它不负责底层数组的分配,所以 data = nil、存储、容量都是 0
    // new的返回值就是该类型的起始内存地址,是没有底层数组
    // 需要通过 append 添加元素,才会开辟底层数组

    func main() {
          var temp []int
          fmt.Println(temp)

          var a []int = make([]int, 2, 4)
          a = append(a, 11, 22)
          fmt.Println(a)

          b := new([]int)
          fmt.Println(b)
          *b = append(*b, 33333)
          fmt.Println(b)
    }


// slice 扩容规则
第一步:
    ccc := []int{1, 2}
    ccc = append(ccc, 333, 444, 555)
    预估容量规则:
        如果"老的容量" * 2 < 需要存储的容量 ---> 新的容量 = 等于预估容量
        否则再细分:
            老元素个数 <  1024 ---> 预估容量直接翻倍
            老元素个数 >= 1024 ---> 预估容量先扩容 1.25 倍
    ccc 是 2 * 2 < 5 个元素,那预估容量就是 5 个元素

第二步 - 内存规格管理:
    内存管理规格以 2 的倍数模式增长, 2、4、8、16、32、48、64

第三步:
    32位操作系统为 4 个字节
    64位操作系统为 8 个字节
    5 个元素 * 8 个字节 = 40 Byte,最接近的是 48 Byte
实际用户得到的是一个 48 字节的内存扩容空间,最多能装 6 个元素,看元素占用的内存大小



new 只分配内存
make用于slice,map,和channel的初始化

new → 内置函数new按指定类型长度分配零值内存,返回指针,并不关心类型内部构造和初始化方式。
make → 内置函数make对引用类型进行创建,编译器会将make转换为目标类型专用的创建函数,以确保完成全部内存分配和相关属性初始化。

Golang内存管理 - GC 标记回收?

GC 三色标记

调一个函数需要传一个结构体,传指针还是传值?

看函数怎么定义,如果需要修改传递的参数,可以通过指针传参,如果单一使用结构体参数,不做修改操作,可以用传值方式

例子:

type B struct {
	Name string
}

func (b B) Test1() {
	fmt.Printf("Test1 addr:%p\n", &b)
	fmt.Printf("Test1 name:%s\n", b.Name)
	b.Name = "john"
}

func (b *B) Test2() {
	fmt.Printf("Test2 addr:%p\n", b)
	fmt.Printf("Test2 name:%s\n", b.Name)
	b.Name = "john"
}

func main() {
	b := B{}
	b.Test1()
	b.Test1()
	b.Test2()
	b.Test2()
}

/*
	Test1 addr:0xc00003a250
	Test1 name:
	Test1 addr:0xc00003a260
	Test1 name:
	Test2 addr:0xc00003a240
	Test2 name:
	Test2 addr:0xc00003a240
	Test2 name:john
*/

Linux中有几种线程模型?Goroutine 的实现原理了解过吗?

线程的实现曾有3种模型:
    多对一(M:1)的用户级线程模型
    一对一(1:1)的内核级线程模型
    多对多(M:N)的两级线程模型

Goroutine 的实现原理:
    S(Sched):结构就是调度器,它维护有存储M和G的队列以及调度器的一些状态信息等。
    M(Machine):一个M直接关联了一个内核线程。
    P(processor):代表了M所需的上下文环境,也是处理用户级代码逻辑的处理器。
    G(Goroutine):其实本质上也是一种轻量级的线程。

GMP模型和PMG模型?

Goroutine 什么时候会发生阻塞?

PMG模型的 Goroutine 有那几种状态,线程有哪几种状态?

答:在PMG模型中 - M去抢占G的时候有个自旋和非自旋状态

线程和协程占用的内存空间分别多少?

答:线程:2M、协程:2KB左右

如果一个 Goroutine 一直占用资源怎么办?PMG模型是怎么解决这问题?

如果若干个线程中,其中一个线程发生了 OOM 会怎么样?如果是 Goroutine 发生了 OOM会怎么样?

如果若干个 Goroutine ,其中有个发生 panic 要怎么处理?

defer 可以捕获到其 Goroutine 的子 Goroutine 发生的 panic吗?

有统一的错误处理吗,统一抛到一个函数处理?类似py的try except

反射了解过吗?反射的原理是什么?

Golang 的锁机制有了解过吗? Mutex的锁有哪几种模式?Mutex 锁底层如何实现? Mutex锁能做自旋锁吗?

RWMutex(读写互斥锁)了解吗?使用什么场景?

Golang 是怎么实现 channel 的底层数据结构?

数据库锁机制
Redis 锁机制、持久化AOF,主从模式、集群模式、哨兵模式
Mysql ORM
负载均衡算法

golang中级面试:

func main(){
  var a uint = 1
  var b uint = 2
  fmt.Println(a-b)
  // 如果操作系统是 32 位就是 2 的 32 次方 -1, 64位操作系统就是 2 的 64 次方 -1
  // Go 是强类型语言,相当于uint 0-1,在计算机uint计算都是加法运算,相当于 0 + -1
  // 负数的计算方式都会转成补码,-1的补码就是所有位都是1,最终计算结果出来都是1的二进制
  // uint识别的话就是当前当前位数的最大值
}

介绍下 rune 类型?
4. uint类型溢出
5. 介绍rune类型
6. 编程题:3个函数分别打印cat、dog、fish,要求每个函数都要起一个goroutine,按照cat、dog、fish顺序打印在屏幕上100次。
7. 介绍一下channel,无缓冲和有缓冲区别
8. 是否了解channel底层实现,比如实现channel的数据结构是什么?
9. channel是否线程安全?
10. Mutex是悲观锁还是乐观锁?悲观锁、乐观锁是什么?
11. Mutex几种模式?
12. Mutex可以做自旋锁吗?
13. 介绍一下RWMutex
14. 项目中用过的锁?
15. 介绍一下线程安全的共享内存方式
16. 介绍一下goroutine
17. goroutine自旋占用cpu如何解决(go调用、gmp)
18. 介绍linux系统信号
19. goroutine抢占时机(gc 栈扫描)
20. Gc触发时机
21. 是否了解其他gc机制
22. Go内存管理方式
23. Channel分配在栈上还是堆上?哪些对象分配在堆上,哪些对象分配在栈上?
24. 介绍一下大对象小对象,为什么小对象多了会造成gc压力?
25. 项目中遇到的oom情况?
26. 项目中使用go遇到的坑?
27. 工作遇到的难题、有挑战的事情,如何解决?
28. 如何指定指令执行顺序?

上一篇:记一次mysql慢查询日志分析


下一篇:linux系统中如何将一行数据转换为一列数据