GoLang逃逸分析讲解

目录
  • 概念
  • 逃逸分析准则
  • 逃逸分析大致思路

概念

当一个对象的指针在被多个方法或者线程引用,称为逃逸分析, 逃逸分析决定一个变量分配在堆上还是栈上, 当然是否发生逃逸是由编译器决定的

分配栈和堆上变量的问题

1.局部变量在栈上(静态分配),函数执行完毕后,自动被栈回收,导致其他对此变量引用出现painc null 指针异常, 栈用户态实现goroutine 作为执行上下文

2.将变量 new 方式分配在堆上(动态分配),堆上有个特点,变量不会被删除,但是会造成内存异常

// 如下代码导致 程序崩溃, 调用栈获取危险的悬挂指针
int *foo ( void)
{
int t = 3;
return &t;
}

1. 栈上分配内存好处: 一般栈内存 2-4 MB
a. 回收快: 减少GC压力,当函数返回回收资源。不需要标记清除
b. 分配快栈分配比堆快,不会有内存碎片
c. 并发快, 清除同步,如果定义对象上有同步锁,却只有一个线程访问,此时逃逸分析机器码 去掉同步锁

总结: 逃逸分析目标:尽可能的使用栈分配内存 go build -gcflags ‘-m -N -l’ 方式编译逃逸分析结果

逃逸分析准则

如果一个函数返回对变量的引用,那么他就发生逃逸

  • 函数外部没有引用,优先分配到栈中(指向栈对象指针不能存在堆中)-- 该指针指向无效值或错误的内存值
  • 函数外部存在引用,必定分配到堆中(指向栈对象指针不能在栈对象回收后存活-- 指向的内存不合法)

CCN_ProLang/CoreGo/GoreGo 下面有对应的文档参考

逃逸分析大致思路

1.最重要函数 escape.go

$GOROOT/src/cmd/compile/internal/gc/escape.go

1. 首先构建一个有向无环图加权图,顶点(语句和表达式分配的变量),边(代表变量之间的赋值关系)
2. 遍历该有向加权图,图中违反上面两个不变条件的赋值路径,算法还记录每个函数的参数到堆的数据流和其返回值的数据流
权重

// p =&q -1 // 最低值
// p =q 0
// p = *q // 解引用 1
// p = **q 2

示例: root =&L , L 节点的指针指向root, 因此 root有一条边,src 就是L,该权重就是 -1

3. 逃逸分析: 分析 分配内存地方与使用 是否发生逃逸
4. go build -gcflags = "-m -m -m -m -W -W -N -l"

1. 当函数中变量返回值, 它将不可能分配在栈上

2.在循环内被重新赋值的变量大部分场景分配在堆上

3.在闭包外声明的变量在闭包内赋值失效后,需要分配在堆上

是否发生逃逸,这一点使用编译器决定的。导致后果:1. GC频繁导致CPU压力大 2.导致性能下降很大

1. 一些逃逸案例:
2. 函数返回变量取地址 导致逃逸
func GetUserInfo(userInfo UserData) *UserData{
   // 编译器判断外部使用 发生逃逸 ,传入的实参对象 取地址类似复制一份
	return &userInfo
}
//修改 将入参修改成指针, 中间没有新结构体没有变化 没有发生逃逸
func GetUserInfo(userInfo *UserData) *UserData {
		return userInfo
}
案例二:不确定类型逃逸
func MyPrintLn(one interface{}) (n int, err error){
	var userInfo = new(User)
	userInfo.name = one // 泛型赋值逃逸 类型转换时候发生逃逸
	return
}
变量确定具体类型
示例三: 间接变量赋值 闭包
var {
     UserOne User // 值对象
     userTwo = new(User) // 引用对象
}
userOne.name = "one" // 不逃逸
userTwo.name = "two" // 逃逸
userOne.age = new(int) // 不逃逸
userTwo.age = new(int) // 逃逸 引用对象在进行引用对象 只能分配堆上
引用对象: 编译器先分析器userTwo 对象分配到堆上,成员变量name,age 引用类型,保证不出现在栈上 导致对象userTwo 被回收 所有 name,age 需要逃逸
优化建议: 不要将引用对象赋值给引用对象

总结

必然不会发生逃逸的情况:

1. 指针被没有发生逃逸的变量引用

2. 仅仅在函数被对变量进行取地址操作,没有将指针传出

一定逃逸

构造函数new/make 返回的指针变量一定逃逸

2. 被已经逃逸指针变量引用指针,一定发生逃逸

3.指针类型是slice,map,chan 引用指针一定发生逃逸

Maybe 逃逸

将指针作为入参传给别的函数,这里看指针在被传入函数的处理过程,如果发生上边三种情况会逃逸,否则不会

到此这篇关于GoLang逃逸分析讲解的文章就介绍到这了,更多相关Go逃逸内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅析Golang中的内存逃逸

    目录 什么是内存逃逸分析 为什么需要逃逸分析 如果变量放错了位置会怎样 内存逃逸场景 return 局部变量的指针 interface{} 动态类型 栈空间不足 闭包 性能 最后 什么是内存逃逸分析 内存逃逸分析是go的编译器在编译期间,根据变量的类型和作用域,确定变量是堆上还是栈上 简单说就是编译器在编译期间,对代码进行分析,确定变量分配内存的位置.如果变量需要分配在堆上,则称作内存逃逸了. 为什么需要逃逸分析 因为go语言是自动自动内存管理的,也就是有GC的.开发者在写代码的时候不需要关心考

  • 浅谈Golang内存逃逸

    目录 1.什么是内存逃逸 2.什么是逃逸分析 3.小结 4.逃逸分析案例 1.函数返回局部指针变量 2.interface类型逃逸 1.interface产生逃逸 2.指向栈对象的指针不能在堆中 3.闭包产生逃逸 4. 变量大小不确定及栈空间不足引发逃逸 5.总结 1.什么是内存逃逸 在一段程序中,每一个函数都会有自己的内存区域分配自己的局部变量,返回值,这些内存会由编译器在栈中进行分配,每一个函数会分配一个栈帧,在函数运行结束后销毁,但是有些变量我们想在函数运行结束后仍然使用,就需要把这个变量

  • 一文搞懂Golang中的内存逃逸

    目录 前言 什么是内存逃逸 查看对象是否发生逃逸 内存逃逸分析的意义 怎么避免内存逃逸 小结 前言 我们都知道go语言中内存管理工作都是由Go在底层完成的,这样我们可以不用过多的关注底层的内存问题,有更多的精力去关注业务逻辑, 但掌握内存的管理,理解内存分配机制,可以让你写出更高效的代码,本文主要总结一下 Golang内存逃逸分析,需要的朋友可以参考以下内容,希望对大家有帮助. 什么是内存逃逸 在了解什么是内存逃逸之前,我们先来了解两个概念,栈内存和堆内存. 堆内存(Heap):一般来讲是人为手

  • GoLang 逃逸分析的机制详解

    对于手动管理内存的语言,比如 C/C++,调用著名的malloc和new函数可以在堆上分配一块内存,这块内存的使用和销毁的责任都在程序员.一不小心,就会发生内存泄露,搞得胆战心惊. 但是 Golang 并不是这样,虽然 Golang 语言里面也有 new.Golang 编译器决定变量应该分配到什么地方时会进行逃逸分析.使用new函数得到的内存不一定就在堆上.堆和栈的区别对程序员"模糊化"了,当然这一切都是Go编译器在背后帮我们完成的.一个变量是在堆上分配,还是在栈上分配,是经过编译器的

  • GoLang逃逸分析讲解

    目录 概念 逃逸分析准则 逃逸分析大致思路 概念 当一个对象的指针在被多个方法或者线程引用,称为逃逸分析, 逃逸分析决定一个变量分配在堆上还是栈上, 当然是否发生逃逸是由编译器决定的 分配栈和堆上变量的问题 1.局部变量在栈上(静态分配),函数执行完毕后,自动被栈回收,导致其他对此变量引用出现painc null 指针异常, 栈用户态实现goroutine 作为执行上下文 2.将变量 new 方式分配在堆上(动态分配),堆上有个特点,变量不会被删除,但是会造成内存异常 // 如下代码导致 程序崩

  • GoLang函数与面向接口编程全面分析讲解

    目录 一.函数 1. 函数的基本形式 2. 递归函数 3. 匿名函数 4. 闭包 5. 延迟调用defer 6. 异常处理 二.面向接口编程 1. 接口的基本概念 2. 接口的使用 3. 接口的赋值 4. 接口嵌入 5. 空接口 6. 类型断言 7. 面向接口编程 一.函数 1. 函数的基本形式 // 函数定义:a,b是形参 func add(a int, b int) { a = a + b } var x, y int = 3, 6 add(x, y) // 函数调用:x,y是实参 形参是函

  • Golang pprof性能测试与分析讲解

    目录 一.性能分析类型 1.CPU性能分析 2.内存性能分析 3.阻塞性能分析 二.cpu性能分析 1.生成pporf 2.分析数据 三.内存性能分析 四.benchmark 生成 profile 一.性能分析类型 1.CPU性能分析 CPU性能分析是最常见的性能分析类型.启动CPU分析时,运行时每隔10ms中断一次,采集正在运行协程的堆栈信息. 程序运行结束后,可以根据收集的数据,找到最热代码路径. 一个函数在分析阶段出现的次数越多,则该函数的代码路径(code path)花费的时间占总运行时

  • 简述Java编程语言中的逃逸分析

    大家一般认为new出来的对象都是被分配在堆上,但这并不是完全正确,通过对Java对象分配过程分析,我们发现对象除了可以被分配在堆上,还可以在栈或TLAB中分配空间.而栈上分配对象的技术基础是逃逸分析和标量替换,本文主要介绍下逃逸分析. 1.逃逸分析的定义 逃逸分析:是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法. 通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上. Java在Java SE

  • Java逃逸分析详解及代码示例

    概念引入 我们都知道,Java 创建的对象都是被分配到堆内存上,但是事实并不是这么绝对,通过对Java对象分配的过程分析,可以知道有两个地方会导致Java中创建出来的对象并一定分别在所认为的堆上.这两个点分别是Java中的逃逸分析和TLAB(Thread Local Allocation Buffer)线程私有的缓存区. 基本概念介绍 逃逸分析,是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法.通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的

  • 面试中遇到的java逃逸分析问题

    前言 记得几年前有一次栈长去面试,问到了这么一个问题: Java中的对象都是在堆中分配吗?说明为什么! 当时我被问得一脸蒙逼,瞬间被秒杀得体无完肤,当时我压根就不知道他在考什么知识点,难道对象不是在堆中分配吗?最后就没然后了,回去等通知了.. 下面我收集了一下网友的回答. 回答很精彩,大家可以加入一起搞技术,我现在将答案总结一下给大家. 什么是逃逸分析? 关于 Java 逃逸分析的定义: 逃逸分析(Escape Analysis)简单来讲就是,Java Hotspot 虚拟机可以分析新创建对象的

  • 深入了解java中的逃逸分析

    逃逸分析 public static StringBuffer craeteStringBuffer(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb; } public static String createStringBuffer(String s1, String s2) { StringBuffer sb = new StringBu

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

    目录 1.逃逸分析介绍 2.Go中内存分配在哪里? 3.Go与C++内存分配的区别 4.逃逸分析骚操作 5.逃逸分析引申示例说明 1.逃逸分析介绍 学计算机的同学都知道,在编译原理中,分析指针动态范围的方法称之为逃逸分析.通俗来讲,当一个对象的指针被多个方法或线程引用时,我们称这个指针发生了"逃逸". Go语言的逃逸分析是编译器执行静态代码分析后,对内存管理进行的优化和简化,它可以决定一个变量是分配到堆还栈上. 写过C/C++的小伙伴应该知道,使用比较经典的malloc和new函数可以

  • Java 数组高频考点分析讲解

    目录 1.数组理论基础 2.常见考点 1.二分查找 2.移除元素 1.数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合,可以通过下标索引的方式获取到下标下对应的数据. 举个栗子(字符数组)~ 可以看到: 1.数组的下标从0开始 2.数组在内存中的地址是连续的 所以在删除元素时,只能用覆盖的方式进行. 例如,要删除下标为2的元素~ 就需要将从2之后的元素依次移到前一个,覆盖掉要删除的元素. 所以删除元素并不是将该元素的空间释放了,而是将后面的元素移到前面,覆盖掉要删除的元素,然后将数组

随机推荐