Go语言中的逃逸分析究竟是什么?

文章编号:5148 资讯动态 2023-10-18

学计算机的同学都知道,在编译原理中,分析指针动态范围的方法称之为逃逸分析。通俗来讲,当一个对象的指针被多个方法或线程引用时,我们称这个指针发生了“逃逸”。

Go语言的逃逸分析是编译器执行静态代码分析后,对内存管理进行的优化和简化,它可以决定一个变量是分配到堆还栈上。

写过C/C的小伙伴应该知道,使用比较经典的malloc和new函数可以在堆上分配一块内存,这块内存的使用和回收(销毁)的任务在程序员中,处理不当,很可能会发生内存泄露。

但是在Go语言中,基本不用担心内存泄露的问题,因为内存回收Go语言中已经帮我们处理了(GC回收机制)。虽然也有new函数,但是使用new函数得到的内存不一定就在堆上。堆和栈的区别对程序员“模糊化”了,当然这一切都是Go编译器在背后帮我们完成的。

Go语言逃逸分析最基本的原则是:如果一个函数返回对一个变量的引用,那么它就会发生逃逸。

简单来说,编译器会分析代码的特征和代码生命周期,Go中的变量只有在编译器可以证明在函数返回后不会再被引用的,才分配到栈上,其他情况下都是分配到堆上。

Go语言里没有一个关键字或者函数可以直接让变量被编译器分配到堆上,相反,编译器通过分析代码来决定将变量分配到何处。

对一个变量取地址,可能会被分配到堆上。但是编译器进行逃逸分析后,如果考察到在函数返回后,此变量不会被引用,那么还是会被分配到栈上。

编译器会根据变量是否被外部引用来决定是否逃逸: Go语言中的逃逸分析究竟是什么?

当我们写C/C代码时,为了提高效率,会经常将pass-by-value(传值)提升成pass-by-reference,企图避免构造函数的运行,并且直接返回一个指针。

你一定还记得,这里隐藏了一个很大的坑:在函数内部定义了一个局部变量,然后返回这个局部变量的地址(指针)。这些局部变量是在栈上分配的(静态内存分配),一旦函数执行完毕,变量占据的内存会被销毁,任何对这个返回值作的动作(如解引用),都将扰乱程序的运行,甚至导致程序直接崩溃。比如下面的这段代码:

int*foo(void){intt=3;return&t;}

有些同学可能知道上面这个坑,用了个更聪明的做法:在函数内部使用new函数构造一个变量(动态内存分配),然后返回此变量的地址。因为变量是在堆上创建的,所以函数退出时不会被销毁。

但是,这样就行了吗?new出来的对象该在何时何地delete呢?调用者可能会忘记delete或者直接拿返回值传给其他函数,之后就再也不能delete它了,也就是发生了内存泄露。关于这个坑,大家可以去看看《EffectiveC》条款21,讲得非常好!

上面讲的C/C中会遇到的问题,在Go中作为一个语言特性被大力推崇,可以解决以上的难点!

C/C中的动态分配的内存需要我们手动来释放,这样会带来一个问题:有些内存处理不当或回收不及时,导致内存泄露。

但是这样的好处是:开发人员可以自己管理内存。

Go的垃圾回收,让堆和栈对程序员保持透明。真正解放了程序员的双手,让他们可以专注于业务,“高效”地完成代码编写。把那些内存管理的复杂机制交给编译器,而程序员可以去享受生活。

逃逸分析这种“骚操作”把变量合理地分配到它该去的地方。即使你是用new申请到的内存,如果我发现你竟然在退出函数后没有用了,那么就把你丢到栈上,毕竟栈上的内存分配比堆上快很多;反之,即使你表面上只是一个普通的变量,但是经过逃逸分析后发现在退出函数之后还有其他地方在引用,那我就把你分配到堆上。

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

堆和栈相比,堆适合不可预知大小的内存分配。但是为此付出的代价是分配速度较慢,而且会形成内存碎片。栈内存分配则会非常快。栈分配内存只需要两个CPU指令:“PUSH”和“RELEASE”,分配和释放;而堆分配内存首先需要去找到一块大小合适的内存块,之后要通过垃圾回收才能释放。

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

引申1:如何查看某个变量是否发生了逃逸?两种方法:使用go命令,查看逃逸分析结果;反汇编源码;

goBuild-gcflags'-m-l'main.go

加-l是为了不让foo函数被内联。得到如下输出

#命令行变量src/main.go:7:9:&tescapestoheapsrc/main.go:6:7:movedtoheap:tsrc/main.go:12:14:*xescapestoheapsrc/main.go:12:13:main...argumentdoesnotescape

foo函数里的变量t逃逸了,和我们预想的一致。让我们不解的是为什么main函数里的x也逃逸了?这是因为有些函数参数为interface类型,比如fmt.Println(a…interface{}),编译期间很难确定其参数的具体类型,也会发生逃逸。

反汇编代码比较难理解,这里就不讲了。

引申2:下面代码中的变量发生逃逸了吗?

packagemaintypeSstruct{}funcmain(){varxS_=identity(x)}funcidentity(xS)S{returnx}

分析:Go语言函数传递都是通过值的,调用函数的时候,直接在栈上copy出一份参数,不存在逃逸。

packagemaintypeSstruct{}funcmain(){varxSy:=&x_=*identity(y)}funcidentity(z*S)*S{returnz}

分析:identity函数的输入直接当成返回值了,因为没有对z作引用,所以z没有逃逸。对x的引用也没有逃出main函数的作用域,因此x也没有发生逃逸。

packagemaintypeSstruct{}funcmain(){varxS_=*ref(x)}funcref(zS)*S{return&z}

分析:z是对x的拷贝,ref函数中对z取了引用,所以z不能放在栈上,否则在ref函数之外,通过引用如何找到z,所以z必须要逃逸到堆上。仅管在main函数中,直接丢弃了ref的结果,但是Go的编译器还没有那么智能,分析不出来这种情况。而对x从来就没有取引用,所以x不会发生逃逸。

还有示例四:如果对一个结构体成员赋引用如何?

packagemaintypeSstruct{M*int}funcmain(){variintrefStruct(i)}funcrefStruct(yint)(zS){z.M=&yreturnz}

分析:refStruct函数对y取了引用,所以y发生了逃逸。

到此这篇关于Go语言中的逃逸分析究竟是什么?的文章就介绍到这了,更多相关语言中的逃逸内容请搜索完美下载以前的文章或继续浏览下面的相关文章希望大家以后多多支持完美下载!

全局中部横幅
360公益

360公益官方平台,由360发起,致力于通过科技创新,为网友提供安全、便捷的公益参与平台,推动公益慈善事业发展。

广东比实希科技有限公司

广东比实希科技有限公司是一家以条码设备制造、条码软件研发为核心的高新技术企业。企业立足于自动化、自动识别领域,以先进的自动化、条码、RFID、技术为核心,坚持走自主研发,品牌化的发展道路,企业拥有十几年的条码设备、条码软件研发、生产、销售、服务经验,并打造了“bsc”标签回卷器、标签剥离机、手持贴标机、条码打印机、条码扫描器系列产品,成为了自动化、自动识别技术应用领域领先的、专业的条码设备、条码软件研发生产制造厂商。

西藏在线

西藏在线网以“让世界了解西藏”为宗旨,是为公众提供西藏综合信息服务的网站。发布涉藏新闻、宗教、人文、旅游、百科、图书、图片、视频、专题、论坛、微博、微信等信息,全面真实地反映西藏的民俗风情。

菱镁水泥网

菱镁商情,菱镁技术,菱镁信息,菱镁标准,菱镁产业大全,菱镁厂商信息权威发布。

佛山市朱雀跨境电商有限公司

佛山市朱雀跨境电商有限公司(广东泓亿科技有限公司)位于广东省,是中国贸易促进委员会淄博委员会下属实体,佛山电子商务协会理事单位。

PC阳光板

佛山市宇迈新材料有限公司座落于中国著名的装饰材料生产基地广东省佛山市,是一家专业从事PC耐力板(实心板)、阳光板(中空板)、蜂窝板、颗粒板等的研发、生产和销售于一体的大型高新、环保企业。

三维吉斯

北京三维吉斯工程软件科技有限公司(BJ3DGIS)是专门从事三维地理信息、矿山、地下工程及工程地理信息三维可视化技术研究与开发的企业,研发的“矿井安全生产综合管理三维可视化信息系统”是当时我国领先应用于矿山安全与生产综合管理的三维可视化信息系统。该系统的开发应用成功,标志着矿井的安全、生产管理、调度指挥、工程设计、地理地测信息由数字化上升到了三维可视化阶段。

医倍达生物科技有限公司

医倍达生物科技有限公司(以下简称“Healthfuture”)是国内一家知名的专业从事高端生物耗材研发、生产和销售的综合性企业,致力于为全球客户提供分子诊断、细胞培养、免疫治疗等方面的高品质耗材及定制化服务。

东光包装机械/东光纸箱机械

沧州鼎麒包装设备有限公司|KCC瓦楞纸板双色印刷开槽机|KCD系列双色印刷开槽机|kcs水性印刷开槽机|dw单面瓦楞纸板生产线

东一创欣.双碳专家

东一创欣促进服务企业产业化快速健康发展,中心是国内最具专业性实操性的综合服务机构之一,中心将依托资源与技术优势,结合实战经验,为企业实现优化升级爆发出新的生命力与企业价值携手共进。

【房屋设计图

✅我爱建房网是一家专业的别墅房屋建筑设计图纸平台网站,有海量的农村自建房设计图、别墅房屋设计图库,提供专业的房屋建筑图纸设计定制及施工服务,我爱建房致力于为广大自建房朋友提供全套别墅图纸设计图纸定制解决方案(涵盖临街门面房商铺方案设计),2023年新款别墅设计网联系电话400-6829-799

龙岩市新丰康佳鲜农产品有限公司【官网】龙岩蔬菜批发,龙岩蔬菜零售,龙岩蔬菜配送,龙岩蔬菜基地种植

龙岩市新丰康佳鲜农产品有限公司专业服务于龙岩蔬菜批发,龙岩蔬菜零售,龙岩蔬菜配送,龙岩蔬菜基地种植,龙岩新鲜猪肉,龙岩哪有新鲜猪肉销售,龙岩新鲜鱼类,龙岩新鲜鱼类批发,龙岩新鲜鱼类销售,龙岩新鲜鸡鸭类,龙岩新鲜鸡鸭类销售,龙岩新鲜鸡鸭类批发,服务热线:13959078063

全局底部横幅