前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试官:简单聊聊 Go 逃逸分析?

面试官:简单聊聊 Go 逃逸分析?

作者头像
后端时光
修改2022-08-25 17:57:36
4310
修改2022-08-25 17:57:36
举报
文章被收录于专栏:后端时光后端时光

本篇文章基于 Golang 1.17.2

又到了金三银四的季节,作为一年一跳槽的胖虎,又准备开始跳槽了,这不他今天又请假来面试了。

“跳跳虎” 对于今天的面试还是有点准备的,八股文早就从网上下载好了,但就是来得及背完,就接到面试邀请了。胖虎心想,“不用怂,人称八股文选手不是白得的,要是今天能唬住面试官就多要点。”

纷争开始了

面试官:“写过C/C++的同学都知道,调用著名的malloc和new函数可以在堆上分配一块内存,这块内存的使用和销毁的责任都在程序员。一不小心,就会发生内存泄漏。那你说下Golang 是怎么处理这个问题的”

胖虎:“Golang 通过逃逸分析,对内存管理进行的优化和简化,它可以决定一个变量是分配到堆上还是栈上。”

什么是golang的逃逸分析

面试官:“那你说下什么是逃逸分析吧”

胖虎想:“这道题我会啊,准备好了吗,我要开始装X了。”

Golang 的逃逸分析,是指编译器根据代码的特征和生命周期,自动把变量分配到堆或者是栈上面。

通过优化了内存管理机制,解放广大程序员的双手。让程序员更关注于业务。

注意:Go 在编译阶段确立逃逸,并不是在运行时。

什么是栈与堆

面试官:“那你说下什么是栈和堆”

胖虎心:“这个也简单啊”

栈( stack)是系统自动分配空间的,例如我们定义一个 char a;系统会自动在栈上为其开辟空间。

而堆(heap)则是程序员根据需要自己申请的空间,例如 malloc(10);开辟十个字节的空间。

先看下内存分配图

栈在内存中是从高地址向下分配的,并且连续的,遵循先进后出原则。系统在分配的时候已经确定好了栈的大小空间。

栈上面的空间是自动回收的,所以栈上面的数据的生命周期在函数结束后,就被释放掉了。

堆分配是从低地址向高地址分配的,每次分配的内存大小可能不一致,导致了空间是不连续的,这也产生内存碎片的原因。由于是程序分配,所以效率相对慢些。

而堆上的数据只要程序不释放空间,就一直可以访问到,不过缺点是一旦忘记释放会造成内存泄露

逃逸分析有什么好处

面试官:“那你说下逃逸分析有什么好处吗”

胖虎:“你是十万个为什么吗?”, 但胖虎还是拿出了自己的看家本领。

就像刚开始提到的,Go 语言中内存的分配不是由程序员自己决定的,而是通过编译阶段确定的分配到何处。这样有什么好处呢?

没错机智的你,可能已经猜到了就是为了优化程序,榨干机器性能,让内存能够得到更高的使用效率。

通过逃逸分析,那些不需要分配到堆上的变量直接分配到栈上,堆上的变量少了不但同时减少 GC 的压力,还减轻了内存分配的开销。

常见的逃逸现象

面试官点点头,称赞的眼光看着胖虎说:“那你再说说,常见的逃逸现象有哪些吧”

胖虎内心崩溃了:“就我一个人一直在这说,都要渴死了,倒是给我来杯水啊,能不能让我喘口气”。但一想 JD 上面给的薪资还是挺诱惑人的,看在💰的份上算了吧。

func(函数类型)数据类型

代码语言:javascript
复制
package main

import "fmt" 

func main() {    
   name := test()    
   fmt.Println(name())
}

func test() func() string {    
   return func() string {
          return "公众号-后端时光"    
   }
}

执行命令

代码语言:javascript
复制
go build -gcflags="-m -l" eee.go

-m:表示内存分析 -l:表示防止内联优化

结果如下,第11行变量 name 逃逸到了堆上

interface{} 数据类型

代码语言:javascript
复制
package main
import"fmt"

func main() {
    name :="Golang"
    fmt.Println(name)
}

同理执行逃逸分析,结果如下, name 变量也逃逸到堆上了

原因是 go/src/fmt/print.go 文件中 Println 方法传参数类型 interface{}, 编译器对传入的变量类型未知,所有统一处理分配到了堆上面去了。

map 数据类型

代码语言:javascript
复制
package main

import "fmt"

func main() {
    name := map[string]string{"name":"胖虎"}
    fmt.Println(name)
}

结果如下,第6行变量 name 逃逸到了堆上

切片数据类型

代码语言:javascript
复制
package main

import "fmt"

func main() {
    name := []string{"胖虎"}
    fmt.Println(name)
}

结果如下,第6行变量 name 逃逸到了堆上

指针 数据类型

代码语言:javascript
复制
package main

import"fmt"

func main() {
    name := point()
    fmt.Println(*name)
}


func point() *string {
    name :="指针"
    return &name
}

结果如下,第11行变量 name 逃逸到了堆上

还有其他情况出现变量逃逸吗?

“额,这……”,胖虎心想:时间太匆忙了,八股文我就背了这么点啊,其他的还么来得及看呢,要是昨天少玩一把游戏就好了。这可怎么办?

看着胖虎憋的满脸通红,面试官笑呵呵的说,“没事的,时间也不早了,今天先到这吧,你还有什么要问我的吗?”

胖虎:“还有哪些会出现变量逃逸啊”

面试官:“channel 或者栈空间不足逃逸, 也会导致逃逸的情况,还有其他问题吗?”

胖虎:“你们双休吗”

面试官:“是的,我们双休,确切来说是一个月休息两天”

胖虎:“啊”

你们说胖虎还要继续面试下去吗?这样的公司能入职吗?

福利

我为大家整理了一些学习资料礼包,关注公众号回复指令:【Golang】、【操作系统】、【Linux】、【LeetCode】、【Golang电子书】即可获得相应学习资料!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端时光 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 纷争开始了
  • 什么是golang的逃逸分析
  • 什么是栈与堆
  • 逃逸分析有什么好处
  • 常见的逃逸现象
  • func(函数类型)数据类型
  • interface{} 数据类型
  • map 数据类型
  • 切片数据类型
  • 指针 数据类型
  • 福利
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档