Go 语言变量逃逸分析

2021-10-23

C/C++中的动态分配的内存需要我们手动释放,经常会忘记释放内存,导致内存泄漏。所以很多现代语言都加上了垃圾回收机制。变量的分配在栈上还是堆上不是由new/malloc决定,而是通过编译器的“逃逸分析”来决定

前置知识

堆与栈

在计算机领域,堆栈是一个不容忽视的概念,是一种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场。

栈的操作及扩展

栈原理:从上往下按顺序放元素,只能从栈顶出,不能从栈底出,遵循先进后出的规则

image.png

堆分配内存和栈分配内存相比,堆适合不可预知大小的内存分配。但是为此付出的代价是分配速度较慢,而且会形成内存碎片。

什么是逃逸分析

在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析程序的哪些地方可以访问到指针。

常见的两种逃逸情景:

  • 函数中局部对象指针被返回

  • 对象指针被多个子程序(线程、协程)共享使用

为什么做逃逸分析

如果变量都分配到堆上,堆不像栈可以自动清理。它会引起Go频繁地进行垃圾回收,而垃圾回收会占用比较大的系统开销(占用CPU容量的25%)。

说白了就是通过逃逸分析,可以尽量把那些不需要分配到堆上的变量直接分配到栈上,堆上的变量少了,会减轻分配堆内存的开销,同时也会减少gc的压力,提高程序的运行速度。

逃逸场景

指针逃逸
 package main
 
 type Team struct {
  Name string
  Num int
 }
 
 func TeamRegister(name string,num int)  *Team{
  s :=new(Team)
 
  s.Name =name
  s.Num=num
 
  return  s
 
 }
 
 func main()  {
  TeamRegister("Warrior",18)
 
 }

函数 TeamRegister() 内部 s 为局部变量,其值通过函数返回值返回,s 本身为一指针,其指向的内存地址不会是栈而是堆。通过运行命令查看逃逸分析日志

[图片上传失败...(image-beb937-1634960627658)]

栈空间不足逃逸(空间开辟过大)
 package main
 
 func Slice()  {
  s :=make([]int,10000,10000)
 
  for index,_ := range s{
  s[index] = index

  }
 
 }
 
 func main()  {
  Slice()
 
 }

函数Slice()分配切片长度为1000时并没有发生逃逸,当分配切片长度为10000就发生了逃逸。当栈空间不足以存放当前对象时或无法判断当前切片长度时会将对象分配到堆中。

通过终端运行逃逸日志查看

image.png
动态类型逃逸(不确定长度大小)

很多函数参数为interface类型,比如fmt.Println(a …interface{}),编译期间很难确定其参数的具体类型,也能产生逃逸。

 package main
 
 import "fmt"
 
 func main()  {
  s :="Escape"
  fmt.Println(s)

 }
image.png
 func F() {
  a := make([]int, 0, 20)     // 栈 空间小
  b := make([]int, 0, 20000) // 堆 空间过大 逃逸

  l := 20
  c := make([]int, 0, l) // 堆 动态分配不定空间 逃逸
 }
image.png
闭包引用对象逃逸
package main
 
 import "fmt"
 
 func series() func() int {
  a,b :=0,1
  return func() int {
  a,b=b,a+b
  return a
  }

 }
 func main()  {
  f:=series()
  for i :=0;i<10;i++{
  fmt.Printf("Series:%d\n",f())
  }
 
 }

运行结果

gosetup

Series:1
Series:1
Series:2
Series:3
Series:5
Series:8
Series:13
Series:21
Series:34
Series:55

逃逸分析

image.png

逃逸总结:

  • 栈上分配内存比在堆中分配内存有更高的效率

  • 栈上分配的内存不需要GC处理

  • 堆上分配的内存使用完毕会交给GC处理

  • 逃逸分析目的是决定内分配地址是栈还是堆

  • 逃逸分析在编译阶段完成

编译器觉得变量应该分配在堆和栈上的原则是:

  • 变量是否被取地址;

  • 变量是否发生逃逸。

参考:https://driverzhang.github.io/post/golang内存分配逃逸分析/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,406评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,395评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,815评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,537评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,536评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,184评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,776评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,668评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,212评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,299评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,438评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,128评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,807评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,279评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,395评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,827评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,446评论 2 359