Added comprehensive Golang interview preparation materials:
- 基础语法(值类型、切片、map、defer、接口、struct、new/make)
- Goroutine 和并发模型(与线程对比、调度模型、内存模型)
- 错误处理和测试(error、panic/recover、单元测试、Benchmark)
- 并发编程进阶(Mutex、RWMutex、WaitGroup、atomic、数据竞争)
- HTTP 和 Web 开发(Client、Server、中间件模式)
- 内存模型和垃圾回收(内存分配、逃逸分析、GC)
- 性能优化(pprof、内存优化、CPU优化、并发优化)
- 反射和 unsafe(反射性能、unsafe 使用场景)
- 接口和类型系统(类型断言、interface{}、类型嵌入、泛型)
- 数据库操作(database/sql、GORM、事务、SQL 注入防护)
- 项目结构和工程化(标准项目结构、Go Module、CI/CD)
Each file includes:
- Detailed questions and comprehensive answers
- Code examples and best practices
- Alibaba P7 level requirements
Total: 60 interview questions (50 backend + 10 Golang)
Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
261 lines
4.3 KiB
Markdown
261 lines
4.3 KiB
Markdown
# Golang 内存模型和垃圾回收
|
||
|
||
## 问题
|
||
|
||
1. Go 的内存分配策略是什么?
|
||
2. Go 的垃圾回收(GC)是如何工作的?
|
||
3. 什么是逃逸分析?
|
||
4. Go 的内存泄漏常见场景有哪些?
|
||
5. sync.Pool 的作用是什么?
|
||
6. 如何优化 Go 程序的内存使用?
|
||
|
||
---
|
||
|
||
## 标准答案
|
||
|
||
### 1. 内存分配策略
|
||
|
||
#### **堆和栈**
|
||
|
||
**栈(Stack)**:
|
||
- 存储局部变量
|
||
- 自动分配和释放
|
||
- 速度快
|
||
|
||
```go
|
||
func foo() int {
|
||
x := 1 // 分配在栈上
|
||
return x
|
||
} // x 自动释放
|
||
```
|
||
|
||
---
|
||
|
||
**堆(Heap)**:
|
||
- 存储动态分配的数据
|
||
- 需要 GC 回收
|
||
|
||
```go
|
||
func foo() *int {
|
||
x := 1
|
||
return &x // x 逃逸到堆上
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### **逃逸分析**
|
||
|
||
**定义**:编译器分析变量的生命周期,决定分配在栈还是堆。
|
||
|
||
**示例**:
|
||
```go
|
||
// ❌ 逃逸到堆
|
||
func foo() *int {
|
||
x := 1
|
||
return &x // x 的引用被返回,必须分配在堆上
|
||
}
|
||
|
||
// ✅ 不逃逸,分配在栈上
|
||
func bar() int {
|
||
x := 1
|
||
return x // x 的值被返回,可以分配在栈上
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2. Go 的垃圾回收
|
||
|
||
#### **GC 算法:三色标记法 + 并发标记清理**
|
||
|
||
**三色标记**:
|
||
- **白色**:未访问
|
||
- **灰色**:已访问,但引用的对象未全部访问
|
||
- **黑色**:已访问,引用的对象也全部访问
|
||
|
||
**流程**:
|
||
```
|
||
1. 根节点标记为灰色
|
||
2. 遍历引用对象,标记为灰色
|
||
3. 递归标记,直到所有可达对象为黑色
|
||
4. 清扫白色对象(垃圾)
|
||
```
|
||
|
||
---
|
||
|
||
#### **GC 触发条件**
|
||
|
||
```go
|
||
// 环境变量
|
||
// GOGC:触发 GC 的堆增长率(默认 100)
|
||
// GOMEMLIMIT:触发 GC 的堆内存上限
|
||
|
||
// 示例
|
||
GOGC=100 // 堆增长 100% 时触发 GC
|
||
GOMEMLIMIT=8192 // 堆上限 8 GB
|
||
```
|
||
|
||
---
|
||
|
||
### 3. 常见内存泄漏场景
|
||
|
||
#### **场景 1:Goroutine 泄漏**
|
||
|
||
```go
|
||
// ❌ 泄漏
|
||
func leak() {
|
||
for i := 0; i < 100; i++ {
|
||
go func() {
|
||
time.Sleep(time.Hour) // 永不退出
|
||
}()
|
||
}
|
||
}
|
||
```
|
||
|
||
**解决**:
|
||
```go
|
||
func noLeak() {
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
defer cancel()
|
||
|
||
for i := 0; i < 100; i++ {
|
||
go func(ctx context.Context) {
|
||
select {
|
||
case <-ctx.Done():
|
||
return
|
||
case <-time.After(time.Hour):
|
||
}
|
||
}(ctx)
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### **场景 2:切片引用**
|
||
|
||
```go
|
||
// ❌ 泄漏
|
||
func leak() {
|
||
for i := 0; i < 1000; i++ {
|
||
s := make([]int, 1024)
|
||
_ = append(s, i)
|
||
// s 被全局变量引用,无法 GC
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 4. sync.Pool
|
||
|
||
#### **作用**
|
||
|
||
**对象池**:复用对象,减少 GC 压力
|
||
|
||
---
|
||
|
||
#### **示例**
|
||
|
||
```go
|
||
var pool = sync.Pool{
|
||
New: func() interface{} {
|
||
return make([]byte, 1024)
|
||
},
|
||
}
|
||
|
||
func process() {
|
||
// 从池中获取
|
||
buf := pool.Get().([]byte)
|
||
|
||
// 使用
|
||
// ...
|
||
|
||
// 归还到池中
|
||
pool.Put(buf)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### **适用场景**
|
||
|
||
- 频繁创建的对象([]byte、Buffer)
|
||
- 临时对象(减少 GC)
|
||
- 对象创建成本高
|
||
|
||
---
|
||
|
||
### 5. 内存优化技巧
|
||
|
||
#### **1. 预分配切片容量**
|
||
|
||
```go
|
||
// ❌ 多次扩容
|
||
s := []int{}
|
||
for i := 0; i < 1000; i++ {
|
||
s = append(s, i) // 多次扩容和拷贝
|
||
}
|
||
|
||
// ✅ 预分配
|
||
s := make([]int, 0, 1000)
|
||
for i := 0; i < 1000; i++ {
|
||
s = append(s, i) // 无扩容
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### **2. 使用 strings.Builder**
|
||
|
||
```go
|
||
// ❌ 字符串拼接(内存拷贝)
|
||
s := ""
|
||
for i := 0; i < 1000; i++ {
|
||
s += fmt.Sprintf("%d", i) // 每次都创建新字符串
|
||
}
|
||
|
||
// ✅ strings.Builder
|
||
var b strings.Builder
|
||
for i := 0; i < 1000; i++ {
|
||
b.WriteString(strconv.Itoa(i))
|
||
}
|
||
s := b.String() // 只分配一次
|
||
```
|
||
|
||
---
|
||
|
||
#### **3. 避免不必要的指针**
|
||
|
||
```go
|
||
// ❌ 使用指针(增加 GC 扫描时间)
|
||
type User struct {
|
||
Name *string
|
||
}
|
||
|
||
// ✅ 使用值类型
|
||
type User struct {
|
||
Name string
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 6. 阿里 P7 加分项
|
||
|
||
**深度理解**:
|
||
- 理解 Go 的内存分配策略(栈、堆、逃逸分析)
|
||
- 理解 Go GC 的三色标记法
|
||
- 理解 GC 的触发条件和调优参数
|
||
|
||
**实战经验**:
|
||
- 有使用 pprof 进行内存分析的经验
|
||
- 有优化内存使用的经验(减少 GC、复用对象)
|
||
- 有处理内存泄漏的经验
|
||
|
||
**性能优化**:
|
||
- 理解如何减少内存分配(sync.Pool、预分配)
|
||
- 理解如何减少 GC 压力(避免指针、复用对象)
|
||
- 理解如何使用 pprof 进行性能分析
|