【面试】用简单直白的语言解释Golang的面试知识点

55 阅读8分钟

一、指针

1.指针是什么

它是一个变量,
它的值为另一个变量的内存地址,
可以使用 & 获取一个变量的指针,
可以使用 * 获取该地址对应的值

二、切片

1.数组和切片的区别是什么

1.数组是值类型,切片是引用类型
2.数组是定长,切片是不定长

2.值类型和引用类型的区别是什么

1.值类型是很多变量各用一份数据
2.引用类型是很多变量共用一份数据

3.切片底层是如何实现的

1.指向底层数组的首地址,用来确定切片从哪里开始访问数组
2.len() 当前能访问的元素数量
3.cap() 切片对底层数组的最大可访问容量

4.切片的扩容方式是怎么样的

1.切片满了时,会分配一个更大的数组,把原来的数据拷贝到新的数组里
2.小容量切片(元素个数〈1024)时,新容量 = 旧容量 * 2
3.大容量切片(元素个数>=1024)时,新容量 = 旧容量 + 旧容量/4

为什么这样扩容:即兼容性能,又防止浪费

5.深拷贝和浅拷贝是什么

浅拷贝:只拷贝表面,内部共享,就像引用类型
深拷贝:表面和内部都拷贝,完全独立 ,就像值类型

三、map

1.map的底层实现

哈希表
桶的索引:用key计算哈希值
值:存储在桶中

2.map是线程安全的吗

不是 多个goroutine同时读写时会出错

3.如何保证map进行并发安全访问

1.使用mutex
2.使用sync.Map

4.sync.map的用法

添加键值对:m.Store
获取元素:m.load
删除元素:m.delete

5.map循环为啥是无序的

元素在桶的位置 由key的哈希值决定,存储顺序不固定

6.nilmap和空map的区别

可以往空map中添加值 往nilmao中田间值会发生panic

7.map的扩容机制是什么

1.map满了就分配更大桶,把元素按哈希搬过去,顺序会变
2.元素数量/桶数量 = 6.5时是阈值 ,超过6.5时会分配更大的桶数组,遍历旧桶,把每个元素重新计算哈希放到新桶

四、函数

调用函数传入结构体时,应该传值还是指针?

指针的副本 也是值(一个新的指针变量) 所以都是值传递

五、select

1.说下select的底层数据结构和特性

select 会把所有 case 封装成 scase(select case)数组
然后进入一个调度逻辑
1.随机打乱顺序(防止饥饿)
2.检查哪个channel可以用
3.如果有就执行对应 send/sev
4.如果都不可用 就把当前goroutine挂起
5.等有一个channel可操作时再唤醒

六、channel

1.说下channel的底层原理

1.本质是 go用来在goroutine之间安全传递数据的管道
2.内部维护 环境缓冲区 + 等待队列 +锁
3.通过阻塞 和 唤醒 goroutine 来保证并发安全

2.有缓冲channel和无缓冲channle的区别

无缓冲channel
1.同步channel 发送和接收操作是阻塞的,直到发送方和接收方都准备好
2.必须有一个接收者在另一端接收数据,否则会造成阻塞
有缓冲channel
1.异步通信
2.缓冲区未满 可以继续往里面写数据

3.向关闭的channel中接收值会返回什么

1.再读取可以得到零值
2.再写入会panic
3.阻塞在 channel 上的 goroutine 会被唤醒

4.channel什么时候会被阻塞

1.向无缓冲channel发送数据 接受者还未做好准备
2.向无缓冲channel接受数据 发送者未做好准备
3.有缓冲channel缓冲区满了,发送着被阻塞
4.有缓冲chnanel接收值,但是缓冲区为空
5.从已关闭的channel中0那个接收值
6.使用select语句

5.channel什么时候会报错

1.往已关闭的channel中发送数据
2.关闭已关闭的channel

七、并发

1.说一下gpm

g:表示一个goroutine,存储着goroutine的信息
p:processor 处理器 G和M的桥梁 维护着一个本地的goroutine队列,负责管理M所需的资源
M:对应操作系统的线程

M必须绑定P才能执行
P优先执行本地的队列 本地执行完后从全局取,保证负载均衡

2.线程 进程 协程的区别

1.线程:最小调度单元 进程的一部分 操作系统直接执行的单位
2.进程:操作系统中一个独立的执行单元
4.协程:用户态线程 由用户控制

3.golang的并发安全是什么 如何确保并发安全

1.使用mutex 互斥锁 确保一个资源只被一个goroutine访问
2.使用原子操作
3.使用channel

八、panic和recover

1.panic和recover的作用

recover捕获panic的异常 确保程序员不会退出

九、锁

1.互斥锁和读写锁的区别

1.互斥锁:同一时间只能有一个线程来访问共享的资源(读写都加锁),读写锁 读可以共享 写加锁
2. 互斥锁:mu.Lock mu.Unlock, 读写锁: RWMu.RLock() RWMu.RUnlock

2.mutex的模式

1.正常模式:锁竞争不激烈时,goroutine按抢到锁的顺序执行
2.饥饿模式:当goroutine等待锁超过阈值时,锁优先给等待最久的goroutine,避免长时间得不到执行

3.自旋是什么

线程尝试获取锁时,不会被阻塞,而是通过循环反复检查锁的状态,直到获取到锁

十、defer

1.defer和return的执行顺序

1.多个defer的执行顺序 后进的先执行
2.先执行defer
3.如果return有返回值 先存在临时变量中
4.然后执行defer
5.然后返回return中的值

2.匿名返回和命名返回的区别

(1)匿名返回

func f() int {
    x := 1
    defer func() {
        fmt.Println("defer执行")
        x++
    }()
    return x
}

执行顺序是:
1️⃣ return x —— 先把返回值准备好(这里是 1)
2️⃣ 执行 defer —— 打印 "defer执行",x 变成 2
3️⃣ 函数真正返回 —— 把刚才准备好的返回值(1)返回

(2)命名返回

func f() (x int) {
    x = 1
    defer func() {
        x++  // 修改了返回值变量
    }()
    return
}

执行顺序:
1️⃣ x = 1
2️⃣ return 准备返回 x
3️⃣ 执行 defer → x = 2
4️⃣ 返回最终 x = 2

十一、接口

1.golang如何实现多态

1.写一个函数,函数的参数为接口类型
2.该参数可以传实现该接口的结构体类型

十二、内存

1.说一下golang的垃圾回收机制

自动内存管理 识别并释放不再使用的内存

特点
1.标记阶段:从根对象(全局变量、栈上引用等)出发,标记还能访问到的对象。
2.清除阶段:清理掉没被标记的对象,释放内存。
3.并发执行:GC 在程序运行时并发执行,不会长时间停顿。

三色:
1.白色:没人用了 等回收
2.灰色:正在检查
3.黑色:有人在用,保留

2. 说一下golang的内存模型

1).自动管理内存
堆上的对象由 GC 自动回收
栈上的对象分配/释放非常快

2).指针与引用
可以访问和共享堆上的数据
跨 goroutine 访问共享数据需要 同步机制(channel、mutex 等)

3).并发与原子操
并发场景下,需要 原子操作或锁 保证安全

4).堆和栈区别
栈:小对象、局部变量,分配快、自动回收
堆:大对象或逃逸对象,由 GC 管理

3.说一下Golang的内存逃逸

本来可以在栈上分配的变量,因为某些原因必须分配到堆上,让它能在函数外或跨 goroutine 使用。

4.什么时候发生内存逃逸

1.局部变量的地址被返回

func f() *int {
    x := 1
    return &x   // x 逃逸到堆
}

2.局部变量的地址被传给其他 goroutine

 func main() {
    x := 1
    go func() {
        fmt.Println(x)  // x 逃逸到堆,保证 goroutine 能安全访问
    }()
}

3.变量被赋值给接口类型或闭包

func f() interface{} {
    x := 1
    return x       // x 可能逃逸到堆
}

5.说一下golang的内存泄漏

1).内存泄漏是什么
程序在运行过程中,不断的分配内存。但是没有释放已经不需要的内存

2).内存泄漏的几种场景
1.未释放动态分配的内存
2.循环引用对象
3.资源未释放
4.缓存没有适时清理或过期
5.全局变量未释放(可以将全局变量设置为nil )

十三、context

1.解释下context的用途

1.控制超时与取消
2.传递请求范围内的元数据
3.协调goroutine的生命周期

2.如何用context包实现goroutine的取消

1.将context作为参数传递给goroutine
2.在goroutine内部可以用context.Done方法检查是否接收到取消信号

3.如何用context包实现goroutine的超时控制

1.作为参数传递给gouritne
2.通过context.Dealline方法来检测是否有超时时间
3.如果时间已到,context.Done方法返回一个非空的通道