来看看现在 go 开发岗 10k 的面试强度

今天继续分享热乎乎的面经,来自一家中小厂的 Go 后端开发岗,薪资是 10k 左右,因为面试问到的问题我觉得都挺有代表性的,所以就想分享出来给大家学习学习:
面经详解
数组和切片的区别
回答:
数组和切片虽然都是存储同类型元素的集合,但核心区别主要体现在以下几个方面:
定义与类型
数组是固定长度的数据结构,声明时必须指定长度,例如
var arr [5]int
,其类型包含长度信息(如[3]int
和[5]int
是不同类型)。• 切片是动态长度的,基于底层数组封装,类型仅包含元素类型(如
[]int
),无需指定长度。切片的结构包含指针(指向底层数组)、长度和容量三个字段。内存分配与传递
数组是值类型,赋值或传参时会发生完整复制,适合需要强隔离的场景(如加密密钥存储)。
切片是引用类型,传递时复制的是切片头(浅拷贝),共享底层数组。修改切片元素会直接影响底层数组。
扩容与性能
数组长度固定,无法扩容;切片支持自动扩容(通过
append
),当容量不足时会创建新的底层数组,并将旧数据复制过去,可能触发性能开销。切片由于动态特性,在高频修改或变长数据场景更灵活,但部分操作(如随机访问)可能略慢于数组。
初始化与使用
数组初始化需指定长度和元素值(如
arr := [3]int{1,2,3}
);切片可通过字面量(slice := []int{1,2,3}
)或从数组/现有切片创建(如slice := arr[1:4]
)。
Go 的 GMP 模型
回答:
GMP 是 Go 语言并发调度的核心模型,由三个核心组件构成:
G(Goroutine)
轻量级协程,初始栈仅 2KB(可动态扩展),由
go
关键字创建。每个 G 包含执行上下文(程序计数器、栈、寄存器状态)。M(Machine)
对应操作系统线程,负责执行 CPU 指令。M 必须绑定一个 P 才能运行 G,默认上限为 10,000 个(可通过
debug.SetMaxThreads
调整)。P(Processor)
虚拟处理器,管理本地 G 队列(Local Queue)和调度上下文。数量由
GOMAXPROCS
控制(通常等于 CPU 核心数)。
调度策略与优化:
Work-Stealing(工作窃取):当 P 的本地队列为空时,会从全局队列或其他 P 窃取 G,避免资源闲置。
Hand Off 机制:若 G 阻塞(如系统调用),M 会释放 P,由其他 M 接管,减少线程阻塞对整体性能的影响。
协作式调度:通过
runtime.Gosched()
或通道操作主动让出 CPU,结合基于信号的抢占式调度(Go 1.14+),减少长耗时 Goroutine 的“饥饿”问题。
优势:GMP 通过细粒度调度和并发执行,实现高吞吐、低延迟的并发处理,特别适合 I/O 密集型和高并发场景(如 Web 服务器)。
defer 关键字的作用以及执行顺序
回答:
defer
关键字用于延迟执行函数,常用于资源释放(如文件关闭、锁解锁)和错误处理,其核心特性如下:
执行顺序
多个
defer
按**后进先出(LIFO)**顺序执行。例如:
这种设计确保依赖资源按逆序释放(如先打开的文件后关闭)。
参数预计算
defer
的函数参数在声明时立即求值,而非执行时。例如:
此时x
的值在defer
声明时已确定为 10。
常见用途
= 资源管理:确保文件、网络连接等资源在函数退出时释放:
错误恢复:结合recover
在defer
中捕获 panic:
日志跟踪:记录函数进入和退出时间,用于性能分析。
性能注意事项
defer
会引入微小性能开销(纳秒级),但在大多数场景可忽略。避免在循环中滥用defer
(可改用显式函数调用)。
有接触过什么 Golang 中的设计模式吗
回答:
单例模式(Singleton)
通过
sync.Once
确保全局唯一实例:
适用于配置管理、日志器等场景。
工厂模式(Factory)
根据输入类型返回不同对象:
隐藏对象创建细节,便于扩展新类型。
策略模式(Strategy)
定义算法族并使其可互换:
适用于支付方式、排序算法等灵活切换的场景。
观察者模式(Observer)
通过事件通知实现松耦合:
用于消息推送、状态变更通知。
装饰器模式(Decorator)
动态扩展对象功能:
适用于中间件、日志增强等场景。
Go 的垃圾回收机制
回答:
Go 的垃圾回收(GC)采用并发三色标记清除算法,主要流程如下:
标记阶段:
初始标记(STW):短暂暂停程序,扫描 GC Roots(全局变量、Goroutine 栈等)。
并发标记:与程序并发执行,遍历对象图,标记存活对象为黑色,引用未扫描完的为灰色,未访问的为白色。
清除阶段:
回收白色对象内存,未标记对象被加入空闲链表供后续分配。
优化技术:
混合写屏障:结合插入屏障(标记新引用对象)和删除屏障(保留旧引用对象),允许并发标记期间减少 STW 时间。
分代假设优化:虽然没有显式分代,但通过局部性原理优化扫描顺序,优先处理年轻对象。
触发条件:
堆内存增长达到阈值(默认 100%,由
GOGC
环境变量控制)。手动调用
runtime.GC()
或内存不足时强制触发。
性能特点:
低延迟:Go 1.14 后 STW 时间通常控制在 1ms 以内,适合实时系统。
吞吐量:相比 Java,Go 的 GC 更轻量,但堆内存可能更大(无分代)。
Go 垃圾回收跟其他语言的有什么区别,STW 跟其他相比做了哪些优化,减少暂停时间
回答:
对比其他语言 GC 机制:
Go 的 STW 优化:
并发标记:标记阶段与用户代码并发执行,仅初始标记和重标记需要极短 STW。
混合写屏障:允许在并发标记期间修改对象引用,无需完全暂停程序。
增量回收:将 GC 工作分摊到多次小规模操作,避免单次长时间停顿。
垃圾回收机制主要针对的是栈区的还是堆区的
回答:
垃圾回收主要针对堆内存:
• 堆区:动态分配的对象(如new
、make
创建)由 GC 管理,通过标记清除回收不可达对象。
• 栈区:栈内存由编译器自动管理(局部变量),函数返回时立即释放,不依赖 GC。
例外情况:若栈上的指针引用了堆对象(如闭包),GC 会通过可达性分析确保堆对象不被错误回收。
使用内存池的优点
回答:
内存池(Memory Pool)通过预分配和复用内存块优化性能,核心优点包括:
减少内存碎片:固定大小块分配避免频繁动态分配导致的内存碎片。
提升分配速度:直接从池中获取内存块,避免系统调用(如
malloc
)的开销。降低 GC 压力:复用对象减少垃圾产生,尤其在高频创建/销毁场景(如网络请求)。
实时性保障:适用于嵌入式或实时系统,确保内存分配时间可控。
适用场景:Web 服务器连接池、游戏对象池、数据库连接管理等。
程序打开之后加载到内存,他有多少个分区
回答:
程序内存通常分为以下区域:
代码区(Text):存储可执行指令,只读。
全局/静态区(BSS+Data):
BSS 段:未初始化的全局/静态变量(默认为零值)。
Data 段:已初始化的全局/静态变量。
堆区(Heap):动态分配内存(如
malloc
、new
),由 GC 或手动管理。栈区(Stack):存储局部变量、函数参数,自动分配释放。
常量区:存储字符串常量等只读数据(可能合并到 Text 或 Data 段)。
TCP 跟 UDP 的区别
回答:
用 TCP 设计文件上传功能,能够知道实时进度,你会怎么设计?
回答:
设计方案:
分片传输:
将文件拆分为固定大小块(如 1MB),每块编号后通过 TCP 发送。
接收方按编号重组文件,确保顺序。
进度反馈:
接收方每收到一个分片,返回 ACK 包含已接收的分片编号和总大小。
发送方根据 ACK 计算进度(已发送分片数/总分片数)。
断点续传:
记录已传输的分片到数据库或文件,中断后从断点继续发送。
流量控制:
滑动窗口机制动态调整发送速率,避免网络拥塞。
优化点:
多线程分片上传提升速度。
压缩分片减少传输量。
客户端本地计算分片哈希,服务端校验完整性。
如何设计缓存,解决缓存和数据库一致性问题
回答:
核心策略:
缓存更新策略:
• 写穿透(Write Through):先更新数据库,再更新缓存。
• 延迟双删:更新数据库后,先删缓存,延迟再删一次(防旧数据回填)。
失效机制:
• 设置合理 TTL,结合惰性删除(访问时检查过期)。
• 主动刷新热点数据,避免批量失效(缓存雪崩)。
并发控制:
• 使用分布式锁(如 Redis 锁),防止缓存击穿(大量请求同时查 DB)。
最终一致性:
• 通过消息队列异步更新缓存(如数据库变更后发送 MQ)。
容错设计:
降级策略:缓存失效时直接返回默认值或限流。
监控告警:缓存命中率、延迟异常时触发告警。
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:infoq 面试群。
版权声明: 本文为 InfoQ 作者【王中阳Go】的原创文章。
原文链接:【http://xie.infoq.cn/article/2bee8f0023b26eac69be8cc84】。文章转载请联系作者。
评论 (1 条评论)