GO语言 逃逸分析

《Go程序员面试笔试宝典》学习笔记

项目地址: https://github.com/fanb129/gopher

​ 在Go语言中,程序员不需要再担心内存泄漏。虽然Go也有内建函数new,但调用new函数得到的内存不一定在堆上,还有可能在栈上。这是因为在Go语言中,堆和栈的区别被模糊化,这一切都是Go编译器在后台完成的。

​ 一个变量是在栈上分配,还是在堆上分配,是经过编译器的逃逸分析之后得到的结论。堆内存分配导致垃圾回收的开销远远大于栈空间分配与释放的开销。

1.1 逃逸分析是什么

C++静态内存分配

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

陷阱:t这个局部变量是在栈上分配的(即静态内存分配),函数执行完毕,变量占据的内存被销毁,任何对这个返回值的操作(如解引用)都将扰乱程序运行。

C++动态内存分配

int *foo(){
    int *t = new int;
    *t = 3;
    return t
}

但是还是有一个问题,调用者可能会忘记删除或者直接将返回值传给其他函数,之后就再也不能删除它了,即发生内存泄漏。

Go语言中

func foo() *int{
    t := new(int)
    *t = 3
    return t
}

没有任何问题

在编译原理中,分析指针动态范围的方法被称之为逃逸分析通俗来讲,当一个对象的指针被多个方法或线程引用时,则称这个指针发生了逃逸。逃逸分析决定一个变量是分配在堆上还是栈上。

1.2 逃逸分析有什么用

​ 逃逸分析把变量合理分配到他该去的地方,内存在退出函数之后就没使用,那就分配到栈上;在函数之外还有其他的地方引用,就分配到堆上。”按需分配“。

​ 堆和栈相比,堆适合不可预知大小的内存分配。但是分配速度较慢,且会形成内存碎片。栈内存分配非常快,只需要PUSH指令,并会自动释放;而堆分配内存首先需要找到一个大小合适的内存块,之后需要通过垃圾回收才能释放,垃圾回收会占用比较大的系统开销。

​ 通过逃逸分析,可以尽量把不需要分配到堆的变量直接分配到栈,堆上的变量少了会减轻堆内存分配的开销,同时减少垃圾回收的压力

1.3 逃逸分析是怎么完成的

基本原则:如果一个函数返回对一个变量的引用,那么这个变量就会发生逃逸。

1)如果变量在函数外部没有引用,则优先放到栈上

2)如果变量在函数外部存在引用,则必定放到堆上

针对第一条,放到堆上的情形:定义了一个很大数组,需要申请的内存过大,超过了栈的存储能力。

1.4 如何确定是否发生逃逸

package main

import "fmt"

// 返回值
func f1() int {
	t1 := 1 // 不发生逃逸
	return t1
}

// 返回引用,指针
func f2() *int {
	t2 := 3 // 逃逸到堆上
	return &t2
}

//  interface{}动态类型逃逸
func f3(x3 *int) {
	fmt.Println(*x3) // *x3逃逸到堆
}

func main() {
	x1 := f1()
	fmt.Println(x1) // x1逃逸到堆
	x2 := f2()
	fmt.Println(*x2) // *x2逃逸到堆
	f3(x2)
}

-gcflags用于启用编译器支持的额外标志

-m 用于输出编译器的优化细节(包括使用逃逸分析这种优化)

-l 用于禁用函数的内联优化,防止逃逸被编译器通过内联彻底的抹除

PS E:\go\gopher\第1章_逃逸分析> go build -gcflags '-m -l' .\demo01.go
# command-line-arguments
.\demo01.go:13:2: moved to heap: t2
.\demo01.go:19:9: x3 does not escape
.\demo01.go:20:13: ... argument does not escape
.\demo01.go:20:14: *x3 escapes to heap
.\demo01.go:25:13: ... argument does not escape
.\demo01.go:25:13: x1 escapes to heap
.\demo01.go:27:13: ... argument does not escape
.\demo01.go:27:14: *x2 escapes to heap

fmp.Println(a ...interface{}),编译期间很难确定其参数的具体类型,也会发生逃逸

1.5 Go与C/C++中的堆和栈是同一个概念吗

  • C/C++中提及的"程序堆栈"本质上是操作系统层级的概念

  • Go程序也是运行在操作系统上的程序,但区别在于传统意义上的"栈"被Go语言运行时全部消耗了,用于维护运行时各个组件之间的协调,调度器\垃圾回收\系统调用等.

  • 对于用户态的Go代码而言,所消耗的"堆和栈"是GO运行时通过管理向系统申请的堆内存,构造逻辑上的"堆和栈",本质都是从操作系统申请而来的堆内存.所以Go程序拥有"几乎"无限的栈内存(1GB),

  • 对于用户态GO代码消耗的栈,GO语言运行时会为了防止内存碎片化,会在适当时候对整个栈进行深拷贝,将其整个复制到另一快内存区域,这也是相较于传统意义上栈是一块固定分配好的内存所出现的另一处差异.

  • 由于这个特点,指针的运算不再凑效,因为再没有特殊说明的情况下,无法确定前后指针所指向的地址的内容是否已经被GO运行时移动.

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
本书针对当前各大it企业面试笔试中常见的问题以及注意事项,进行了深层次的分析。本书除了对传统的计算机相关知识(c/c++、数据结构与算法、操作系统、计算机网络与通信、软件工程、数据库、智力题、英语面试等)进行介绍外,还根据当前计算机技术的发展潮流,对面试笔试中常见的海量数据处理进行了详细的分析。同时,为了更具说服力,本书特邀多位it名企面试官现身说法,对面试过程中求职者存在的问题进行了深度剖析,同时本书引入了一批来自于名牌高校、就职于明星企业的职场达人的真实求职案例,通过他们的求职经验与教训,抛砖引玉,将整个求职过程生动形象地展示在读者面前,进而对求职者起到一定的指引作用。本书也对各种类型的it企业的招聘环节进行了庖丁解牛式的分析,帮助求职者能够更加有针对性地 进行求职准备。 本书是一本计算机相关专业毕业生面试笔试的求职用书,同时也适合期望在计算机软硬件行业大显身手的计算机爱好者阅读。 程序员面试笔试宝典 目录 前言 上篇 面试笔试经验技巧篇 第1章 面试官箴言 2 第2章 面试心得交流 9 第3章 企业面试笔试攻略 20 第4章 面试笔试技巧 42 第5章 英文面试攻略 82 第6章 智力题攻略 102 下篇 面试笔试技术攻克篇 第7章 程序设计基础 122 第8章 数据库 240 第9章 网络与通信 254 第10章 操作系统 270 第11章 软件工程 278 第12章 发散思维 289 第13章 数据结构与算法 295 第14章 海量数据处理 390
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值