当前位置: 首页 > article >正文

Goland 内存逃逸问题

内存逃逸是什么?

在go语言中,内存分配存在两个方式:堆分配;栈分配。

栈分配:是在函数调用时为局部变量分配内存,当函数返回时,这些内存会自动释放。

堆分配:通过 new 或者 make 函数动态分配内存,需要手动进行释放或者自动回收机制释放。

内存逃逸是指原先在栈上分配的内存被分配到堆上。这样导致函数结束时不能自动回收,只能通过垃圾回收器回收,对于性能影响较大。

内存逃逸的几种情况

1.返回指针导致内存逃逸

package main

import "fmt"

func createPointer() *int {
	x := 100 // 局部变量
	return &x // x 逃逸到堆上
}

func main() {
	p := createPointer()
	fmt.Println(*p) // 100
}

为什么发生了逃逸?

  • xcreatePointer() 的局部变量,正常情况下函数返回后应该销毁
  • 但是 &x 返回了 x 的地址,导致 x 需要在 main() 继续使用,不能放在栈上,必须逃逸到堆

2.切片或 map 赋值导致逃逸

package main

func main() {
	s := make([]int, 3) // 堆分配
	m := make(map[int]int) // 堆分配
	_ = s
	_ = m
}

为什么发生了逃逸?

  • make([]int, 3) 创建了切片,其底层数组可能存储在堆上(具体取决于大小)
  • make(map[int]int) 创建的 map 结构存储在堆上,因为 map 需要在多个作用域间传递。

3.字符串和 interface{} 赋值导致逃逸

package main

import "fmt"

func printAny(i interface{}) {
	fmt.Println(i)
}

func main() {
	x := "hello"
	printAny(x) // x 逃逸到堆
}

为什么发生了逃逸?

  • xstring,但 printAny() 需要 interface{}
  • Go 需要将 x 装箱(boxing),创建 interface{} 类型的值,而 interface{} 可能存储在堆上

4.闭包捕获变量导致逃逸

package main

import "fmt"

func closure() func() int {
	x := 42
	return func() int {
		return x // x 逃逸到堆
	}
}

func main() {
	f := closure()
	fmt.Println(f()) // 42
}

为什么发生了逃逸?

  • xclosure() 的局部变量,但被匿名函数 func() int 捕获
  • 由于 func() int 可能在 closure() 结束后仍然被调用,x 必须分配到堆上

如何避免内存逃逸?

避免返回指针

错误情况

// ❌ 发生逃逸
func bad() *int {
    x := 42
    return &x
}

改进(使用值返回,而不是指针) 

func good() int {
    x := 42
    return x // 不逃逸
}

使用参数传递而不是使用闭包

func returnFunction(x int) func() int {
    return func() int { return x }
}

 使用 sync.Pool 复用对象

package main

import (
    "fmt"
    "sync"
)

var pool = sync.Pool{
    New: func() interface{} { return new(int) },
}

func main() {
    p := pool.Get().(*int) // 复用对象,减少堆分配
    *p = 100
    fmt.Println(*p)
    pool.Put(p) // 归还对象
}

 使用 strings.Builder 代替字符串拼接

package main

import (
    "fmt"
    "strings"
)

func main() {
    var sb strings.Builder // 避免字符串逃逸
    sb.WriteString("Hello ")
    sb.WriteString("World")
    fmt.Println(sb.String())
}

总结

逃逸原因例子解决方案
返回指针return &x返回值而不是指针
切片/Mapmake([]int, 3)小数组可用 var arr [3]int
interface{}printAny(x)避免不必要的 interface{}
闭包捕获变量return func() { return x }使用 参数传递 而不是 闭包
字符串拼接s += "hello"strings.Builder


http://www.kler.cn/a/540581.html

相关文章:

  • 2025.2.9 每日学习记录2:技术报告写了一半+一点点读后感
  • DeepSeek与GPT大语言模型教程
  • 线程上下文-ThreadLocal原理
  • 移植BOA服务器到GEC2440开发板
  • 1.2 变革里程碑:Transformer 的崛起
  • 如何在 Linux 中管理自定义脚本:将 ~/bin 目录添加到 $PATH
  • 移动(新)魔百盒刷机教程[M301A_YS]
  • 用AI写游戏2——实现老虎机游戏
  • .NET周刊【2月第1期 2025-02-02】
  • DeepSeek 关联 Word 使用教程:解锁办公新效率
  • 不知道MySQL密码怎么办?|不卸载重装的处理办法
  • Unity-Mirror网络框架-从入门到精通之EdgegapLobby示例
  • 用大模型学大模型02-数学基础
  • Spring框架学习大纲
  • R 数组:高效数据处理的基础
  • python+open3d实现彩色点云的无堵塞动态可视化连续播放
  • YOLOv11实战海洋动物图像识别
  • matlab simulink 模拟光伏电池板在不同光照下的输出功率曲线
  • 集群服务 | 云微服务 | 快速入门
  • Pycharm使用Anaconda创建的不同conda环境
  • 03-DevOps-安装并初始化Gitlab
  • torch_bmm验算及代码测试
  • 38.社区信息管理系统(基于springboothtml)
  • windows10 wsa 安卓子系统终结版
  • 网络协议课程笔记上
  • AUTOSAR 4.2.2版本中Dem 操作循环(Operation Cycle)的开启和关闭