Golang基础面试题

1.进程、线程、goroutine 三者的区别与联系?从:资源占用、调度、开销、并发能力这几个方面对比。

  1. 进程
     
    • 系统资源分配的最小单位,有独立内存空间。
    • 开销最大,调度切换成本高。
    • 量级:GB 级别资源。
     
  2. 线程
     
    • 进程内的执行单元,共享进程资源。
    • 由操作系统内核调度,栈默认 1~8MB。
    • 切换成本较高,不宜过多。
     
  3. goroutine(Go 协程)
     
    • Go 运行时管理的用户态轻量级协程。
    • 初始栈仅 2KB,动态扩缩容。
    • 调度切换在用户态完成,成本极低。
    • 单机轻松十万、百万级并发。
     
一句话总结:
 
进程是资源容器,线程是内核执行单元,goroutine 是 Go 实现的超高并发轻量级执行单元。

2.请你解释一下 Go 里的 GMP 模型是什么?
G、M、P 分别代表什么,它们之间是怎么协作的?

G、M、P 分别是什么

 
  • G (Goroutine):用户任务,就是你写的 go func(),存栈、指令、状态。
  • M (OS Thread):内核线程,真正被操作系统调度执行。
  • P (Processor):逻辑处理器,持有 G 队列、调度上下文,用来把 G 绑定到 M 上运行。
 

协作流程

 
  1. 每个 P 维护一个本地 G 队列。
  2. M 必须绑定 P 才能运行 G。
  3. 一个 M 同一时间只跑一个 G。
  4. G 遇到阻塞(IO、channel、锁)时,M 会解绑 G,去拿 P 里的下一个 G 运行。
  5. P 本地队列为空时,会从全局 G 队列或其他 P 偷 G(work stealing)。
 

 一句话满分版
G 是 goroutine 任务,M 是操作系统线程,P 是逻辑处理器。
M 必须绑定 P 才能执行 G,P 管理 G 队列,Go 调度器通过 GMP 实现 M:N 调度,支持高并发。
 

3.Go 里 make 和 new 的区别是什么?

New 用于任意类型,返回返回指向零值内存的指针。 make 仅用于 slice、 map、 channel,返回已初始化的值本身

4.Go 里面 slice 的底层结构是什么?slice 扩容机制大概是怎样的?

slice 底层是一个结构体,包含指针 ptr、长度 len、容量 cap。

 

扩容机制:

 
  1. 原容量 < 1024 时,新容量 = 旧容量 × 2;
  2. 原容量 ≥ 1024 时,新容量 = 旧容量 × 1.25;
  3. 再根据元素大小、内存对齐做向上取整。

5.Go 里 map 的底层结构是什么?为什么遍历 map 是无序的?

Map 的底层结构是个哈希表。当键值被添加到 map 中时,键会通过哈希函数计算出一个哈希值,这个哈希值决定了该键值对儿在底层数据桶中的存储位置。 Go 语言为了防止拒绝服务 DOS 攻击引入了随机化哈希函数。这意味着每次程序运行时,或者在某些情况下,即使微小的代码或者环境变化,哈希函数计算出来哈希值序序列也可能有略有不同,导致键值在哈希表上的存储顺序发生变化。

6.Go 里面 channel 有什么用?有缓冲 channel 和无缓冲 channel 的区别是什么?

channel 作用

 
  1. 用于 goroutine 之间的通信;
  2. 实现 同步 和 并发控制;
  3. 遵循 Go 理念:不要通过共享内存来通信,要通过通信来共享内存。
 

有缓冲 vs 无缓冲

 
  • 无缓冲 channel
    • 发送方会阻塞,直到接收方接收;
    • 相当于同步。
     
  • 有缓冲 channel
    • 缓冲区未满前,发送不阻塞;
    • 缓冲区未空前,接收不阻塞;
    • 相当于异步。
    • 7.Go 里 panic 和 recover 怎么用? 什么场景下应该用,什么场景不应该用?
     

panic

  • 主动抛出异常,中断当前执行流程。
  • 会触发defer,然后程序崩溃退出。
 

recover

 
  • 只能在defer 函数里使用,用来捕获 panic,恢复正常执行。
  • 捕获后程序不会崩溃。
 

使用场景

  • 应该用:真正的、不可恢复的程序错误(如程序逻辑 BUG、资源严重不足)。
  • 不应该用:业务错误(如参数不合法、查询不到数据),业务错误应该用 error。

8.Go 中 interface 空接口和非空接口有什么区别?interface{} 可以存什么?

 

空接口 interface{}

 
  • 没有定义任何方法
  • 所有类型都默认实现空接口
  • 可以存任意类型的值,类似泛型容器
 

非空接口(有方法的 interface)

 
  • 定义了一组方法列表
  • 只有实现了所有方法的类型,才算是实现了该接口
  • 用于约束行为、多态、解耦
 

底层结构区别(面试加分)

 
  • 空接口底层:eface,只存 类型 + 数据指针
  • 非空接口底层:iface,存 接口信息 + 类型 + 数据指针
 

第 9 题Go 中 defer 的执行顺序是什么?defer 在什么时机执行?有哪些注意事项?

 

1. 执行顺序

 
后进先出(栈),越晚声明的 defer 越早执行。
 

2. 执行时机

 
在函数 return 之后、真正返回调用方之前执行。
 

3. 重要注意事项(必背)

 
  1. defer 中参数是预计算的,不是执行时才计算
  2. defer 可以捕获 panic,配合 recover 使用
  3. 循环里慎用 defer,容易延迟释放资源或堆积
  4. 有名返回值 + defer 配合时,可以修改返回值

 10.Go 怎么实现并发安全?常用方案有哪些?

Go 实现并发安全的常用方案

 
  1. 互斥锁 sync.Mutex
     
    独占锁,同一时间只有一个 goroutine 能进入临界区。
     
  2. 读写锁 sync.RWMutex
     
    • 读锁:可共享,多个 goroutine 同时读
    • 写锁:独占
       
      适合读多写少场景。
     
  3. channel 通道
     
    通过通信来共享内存,Go 推荐的并发安全方式。
     
  4. 原子操作 sync/atomic
     
    针对数值、指针的简单原子操作,性能最高。
     
  5. sync.Once
     
    保证某段代码只执行一次,常用于单例初始化。
     
  6. sync.WaitGroup
     
    等待一组 goroutine 全部执行完成。
     
 

 
posted @ 2026-03-12 19:07  Mr.peter  阅读(219)  评论(0)    收藏  举报