GO性能优化指北-高效内存分配

本文探讨了Go语言中的内存分配优化,尤其是栈分配与堆分配的区别和影响。通过逃逸分析,Go编译器决定变量是在栈还是堆上分配。文章提到了减少堆分配以提升性能,分析了何时会发生堆分配,如值传递、channel、切片、接口使用等场景,并给出了一些优化技巧,包括避免返回string、减少接口使用等。
摘要由CSDN通过智能技术生成

绝大多数时候我们不需要关注内存管理, go运行时会自行处理, 但是对于热点路径, 我们必须确保高效地内存分配来榨取每一点性能

就内存分配而言, 有两件事情可以帮助我们提升性能: 1. 减少分配, 如将fmt.Sprintf改为fmt.Fprintf来避免创建新的字符串或者尽量使用[]byte而不是string来重用 2. 尽量避免在堆上的分配, 分配在堆的内存会增加GC花费从而降低性能, 本文将试图对此继续分析

需要提醒的是, 一切的性能优化都必须以性能分析为起点, 绝对不能进行未测量的优化.

堆分配与栈分配花销对比

通常而言, 堆分配的花销是远比栈分配更昂贵的. 因此高效的内存分配一定要尽量降低不必要的堆分配1. 认真分析的话, 栈分配和堆分配的花销

栈分配的花销比较简单, 仅仅需要两条CPU指令: 一个是推入栈中(来分配), 一个是从栈中释放.

堆分配的花销则主要在于分配和GC: malloc需要寻找一个足够大的闲置空间来存储, 而GC也需要扫描堆来发现可回收的对象. 这两种操作的时间成本显然高于栈分配的花销

什么情况下会触发堆分配?

在很多其他语言里面, 一个值会被分配在栈还是堆是清晰的:对于JAVA来说, 所有的对象(object)都会被分配至堆, 所有的基础类型都会分配在栈. 对于C/C++来说, 使用new创建的值就会被分配在堆, 否则默认是栈. 然而在GO中, 事情会变得更加复杂点.

比方说, 对于下面的代码段, 你是否能确定它分配在栈还是堆?

type user struct {
   
	name string
	age int
}
...
u:=user{
   
		name:"li",
		age:15,
}

答案是, 你不能, 你需要更多的信息判断. 比方说如果是下面的完整代码, 那么u会被分配在栈

func test()user{
   
   u:=user{
   
      name:"li",
      age:15,
   }
   return u
}

而如果是下面的代码, u就会被分配至堆

func test()*user{
   
   u:=user{
   
      name:"li",
      age:15,
   }
   return &u
}

在GO中, 一个值是否被分配至堆还是栈, 取决于GO编译器对其做的逃逸分析结果. 如果一个变量的生命周期和大小可以在编译时确定, 那么该变量就会栈分配, 否则就会触发堆分配.

这样做的目的有两点:

  1. 降低心智负担. 如果需要自行决定是否分配至堆, 那么往往我们还需要考虑怎么释放堆资源. (也就是没有GC, 类似于C++和C)
  2. 降低GC负担, 因为逃逸分析可以避免将所有对象都分配至堆中.

尽管我们无法实际控制分配至堆还是栈, 但是我们仍然有必要了解, 什么情况下值会被分配至堆, 以及对此我们有什么绕过去的办法.

下面列举的是常见的堆分配的例子, 有一些可能意料之中, 而有一些也许会出乎意料:他们也是性能优化的目标

跨越栈帧(stack frame)的值传递

最为常见也为大家所理解的情况, 莫过于下面的类似例子

func New()*user{
   
   u:=user{
   
      name:"li",
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值