go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的生命周期:变量逃逸分析 —— go编译器自动决定变量的内存分配方式(堆还是栈),提高程序运行效率

Posted Locutus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的生命周期:变量逃逸分析 —— go编译器自动决定变量的内存分配方式(堆还是栈),提高程序运行效率相关的知识,希望对你有一定的参考价值。

C/C++语言需要开发者自己管理内存分配,选择合适的内存分配方式以适应不同算法需求:函数的局部变量尽量使用栈内存分配;全局变量、结构体成员变量使用堆内存分配等。

变量逃逸分析:go语言把内存分配的过程整合到编译器中,由go编译器分析代码的特征和生命周期,自动选择堆分配内存还是栈分配内存。上述过程对程序员是透明的,不会感知,不需要耗费精力。编译器决定选择堆分配还是栈分配的原则。

  • 变量是否发生逃逸
  • 变量是否被取地址

变量逃逸分析(使用哪种内存分配机制)

package main

import (
	"fmt"
)

// 测试函数入参和返回值
func dummy(a int) int {
	var b int  // 声明局部变量(临时变量)
	b = a

	return b   // 返回临时变量
}

// 空函数,没有任何入参和返回值
func void() {}

func main() {
	var c int  // 测试在main函数中的变量分析
	void()

	fmt.Println(c, dummy(0))
}
// -gcflags是编译参数,-m表示进行对内存分配分析,-l表示避免程序内联优化
# go run -gcflags "-m -l" var_escape.go    

./var_escape.go:21:13: c escapes to heap
./var_escape.go:21:22: dummy(0) escapes to heap
./var_escape.go:21:13: main ... argument does not escape
0 0

b是整型,通过函数dummy()返回,逃出了dummy()函数(dummy(0) escapes to heap);使用堆heap分配,给变量c分配内存。


取变量的内存地址时,进行变量逃逸分析

package main

import "fmt"

// 空结构体
type data struct{}

func dummy() *data {
	var c data // 声明c为data结构体类型,基本的实例化,是值类型

	return &c // 返回函数局部变量的内存地址,是*data类型指针
}

func main() {
	fmt.Println(dummy()) //打印dummy函数的返回值
}
# go run -gc flags "-m -l" addrs_escape.go

#command-line-arguments
./addrs_escape.go:11:9: &c escapes to heap
./addrs_escape.go:9:6: moved to heap: c
./addrs_escape.go:15:19: dummy() escapes to heap
./addrs_escape.go:15:13: main ... argument does not escape
&{}

把c移到heap堆中。go最终选择把c的data结构分配到堆上,然后由GC去回收c的内存。

以上是关于go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的生命周期:变量逃逸分析 —— go编译器自动决定变量的内存分配方式(堆还是栈),提高程序运行效率的主要内容,如果未能解决你的问题,请参考以下文章

go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 常量变量的声明:单个变量的声明与赋值

go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的数值类型转换

go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 常量变量的声明:变量初始化声明和变量赋值

go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 常量变量的声明:多个变量的初始化声明与赋值

go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的类型零值

go语言学习笔记 — 基础 — 基本语法 — 常量与变量 — 变量的生命周期:堆(heap)