Files
interview/questions/13-Golang语言/go-memory-model.md
yasinshaw ab3a99f131 feat: add 10 Golang interview questions
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>
2026-03-01 00:16:36 +08:00

4.3 KiB
Raw Blame History

Golang 内存模型和垃圾回收

问题

  1. Go 的内存分配策略是什么?
  2. Go 的垃圾回收GC是如何工作的
  3. 什么是逃逸分析?
  4. Go 的内存泄漏常见场景有哪些?
  5. sync.Pool 的作用是什么?
  6. 如何优化 Go 程序的内存使用?

标准答案

1. 内存分配策略

堆和栈

Stack

  • 存储局部变量
  • 自动分配和释放
  • 速度快
func foo() int {
    x := 1  // 分配在栈上
    return x
}  // x 自动释放

Heap

  • 存储动态分配的数据
  • 需要 GC 回收
func foo() *int {
    x := 1
    return &x  // x 逃逸到堆上
}

逃逸分析

定义:编译器分析变量的生命周期,决定分配在栈还是堆。

示例

// ❌ 逃逸到堆
func foo() *int {
    x := 1
    return &x  // x 的引用被返回,必须分配在堆上
}

// ✅ 不逃逸,分配在栈上
func bar() int {
    x := 1
    return x  // x 的值被返回,可以分配在栈上
}

2. Go 的垃圾回收

GC 算法:三色标记法 + 并发标记清理

三色标记

  • 白色:未访问
  • 灰色:已访问,但引用的对象未全部访问
  • 黑色:已访问,引用的对象也全部访问

流程

1. 根节点标记为灰色
2. 遍历引用对象,标记为灰色
3. 递归标记,直到所有可达对象为黑色
4. 清扫白色对象(垃圾)

GC 触发条件

// 环境变量
// GOGC触发 GC 的堆增长率(默认 100
// GOMEMLIMIT触发 GC 的堆内存上限

// 示例
GOGC=100      // 堆增长 100% 时触发 GC
GOMEMLIMIT=8192  // 堆上限 8 GB

3. 常见内存泄漏场景

场景 1Goroutine 泄漏

// ❌ 泄漏
func leak() {
    for i := 0; i < 100; i++ {
        go func() {
            time.Sleep(time.Hour)  // 永不退出
        }()
    }
}

解决

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切片引用

// ❌ 泄漏
func leak() {
    for i := 0; i < 1000; i++ {
        s := make([]int, 1024)
        _ = append(s, i)
        // s 被全局变量引用,无法 GC
    }
}

4. sync.Pool

作用

对象池:复用对象,减少 GC 压力


示例

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. 预分配切片容量

// ❌ 多次扩容
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

// ❌ 字符串拼接(内存拷贝)
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. 避免不必要的指针

// ❌ 使用指针(增加 GC 扫描时间)
type User struct {
    Name *string
}

// ✅ 使用值类型
type User struct {
    Name string
}

6. 阿里 P7 加分项

深度理解

  • 理解 Go 的内存分配策略(栈、堆、逃逸分析)
  • 理解 Go GC 的三色标记法
  • 理解 GC 的触发条件和调优参数

实战经验

  • 有使用 pprof 进行内存分析的经验
  • 有优化内存使用的经验(减少 GC、复用对象
  • 有处理内存泄漏的经验

性能优化

  • 理解如何减少内存分配sync.Pool、预分配
  • 理解如何减少 GC 压力(避免指针、复用对象)
  • 理解如何使用 pprof 进行性能分析