一、背景
逃逸分析,这个是个什么鬼,干什么用的,要如何使用,对于半路出家的我看到这个词真是一脸MB,于是秉持这 What,Why,How的思路开启了百度之路在此记录下历程
二、WHAT
a) 什么是逃逸分析? 逃逸分析,是一种可以有效减少程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。 逃逸分析我个人理解就是一个变量到底是存在堆上还是栈上的分析,有利于内存的使用优化同时也可以降低gc的压力
b) 逃逸分析的原理? 我是这么理解的,为了减少临时对象在堆内分配的数量,我会在一个方法体内定义一个局部变量,并且该变量在方法执行过程中未发生逃逸,
相对官方解释是:go语言编译器的逃逸分析就是当发现变量的作用域没有跑出变量的范围就可以在栈上(一般情况下是这样),相反则会分配到堆上,另外如果局部变量非常大也会被分配到堆上,让堆和栈对程序员保持透明。真正解放了程序员的双手,让他们可以专注于业务“高效”地完成代码编写,而把那些内存管理的复杂机制交给编译器。逃逸分析把变量 合理地分配到它该去的地方,“找准自己的位置”。即使是用new函数申请到的内存,如果编译器发现这块内存在退出函数后就没有使用了,那就分配到栈上,毕竟栈上的内存分配比堆上快很多:反之,即使表面上只是一个普通的变量,但是经过编译器的速速分析后发现,在函数之外还有其他的地方在引用,那就分配到堆上。真正地做到“按需分配”。如果变量都分配到堆上,堆不像栈可以自动清理。就会引起Go频繁地进行垃圾回收,而垃圾回收会占用比较大的系统开销。堆和栈相比,堆适合不可预知大小的内存分配。但是为此付出的代价是分配速度较慢而且会形成内存碎片,栈内存分配则会非常快,栈分配内存只需要通过RUSLS指令,并且会自动释放;而堆分配内存首先需要去找到一个大小会适的内存块,之后要通过垃圾回收才能释放,通过逃逸分析可以尽量把这些不需要分配到堆上的变量直接分配到栈上,堆上的变量少了会减轻对内存的分配开销,同时也会减少垃圾回收的压力,提高程序运行速度.
三、WHY
a) 为什么“逃逸”? 在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他方法或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。通俗点讲,如果一个对象的指针被多个方法或者线程引用时,那么我们就称这个对象的指针(或对象)的逃逸
四、HOW
a) 逃逸的方式? 方法逃逸:在一个方法体内,定义一个局部变量,而它可能被外部方法引用,比如作为调用参数传递给方法,或作为对象直接返回。或者,可以理解成对象跳出了方法。
线程逃逸:这个对象被其他线程访问到,比如赋值给了实例变量,并被其他线程访问到了。对象逃出了当前线程.
b) 逃逸分析的好处? 如果一个对象不会在方法体内,或线程内发生逃逸(或者说是通过逃逸分析后,使其未能发生逃逸)
1. 栈上分配:
一般情况下,不会逃逸的对象所占空间比较大,如果能使用栈上的空间,那么大量的对象将随方法的结束而销毁,减轻了GC压力
2. 同步消除:
如果你定义的类的方法上有同步锁,但在运行时,却只有一个线程在访问,此时逃逸分析后的机器码,会去掉同步锁运行。
3. 标量替换
go中的原始数据类型(int,long等数值类型等)都不能再进一步分解,它们可以称为标量。相对的,如果一个数据可以继续分解,那它称为聚合量,go中最典型的聚合量是对象。如果逃逸分析证明一个对象不会被外部访问,并且这个对象是可分解的,那程序真正执行的时候将可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来代替。拆散后的变量便可以被单独分析与优化,可以各自分别在栈帧或寄存器上分配空间,原本的对象就无需整体分配空间了。
- 当然我们GO语言的学习和开发过程中已经把堆和栈给”模糊化”,这一切都是Go编译器在后台帮我们完成,我们在使用中不用去考虑这个变量到底保存在哪里,其实这些一定规则的不过都是经过编译器逃逸分析后得出来的结论
五、我的DEMO
a) 使用go相关的命令 go build -gcflags '-m -l' main.go 运行一下代码
i. 得到一下结果
b) 已上截图中显示ty发生了逃逸的同时jiangyou也发生了逃逸,导致jiangyou也逃逸的原因则是打印函数fmt.Println中的参数为interface,因为类型的不确定所以就有了逃逸
c) 使用go tool compile -S main.go 命令(这个是反汇编结果我也讲不明白只知道是这么分析)!
d) 结果显示ty通过newobject函数已经被分配至堆上,也就发生了逃逸
六、总结
a) 正是有了编译器的逃逸分析才有了我们现在使用new函数时不用担心内存泄露等问题的出现也解放了我们的双手专心写代码