golang变量堆栈分析

代码如下:

package main

func main() {
    
    
	var val1 int8 = 1
	var val2 int8 = 2
	var val3 int8 = 2
	var val4 int8 = 2
	var val5 int8 = 2
	// 输出
	// 说明 此处内存是连续的
	// 0xc00003c76f 0xc00003c76e 0xc00003c76d 0xc00003c76c 0xc00003c76b
	for i := 0; i < 1; i++ {
    
    
		println(&val1, &val2, &val3, &val4, &val5, "main")
	}

	// 输出,此处foo1函数内联, 内存地址和val1中的交替
	//0xc00003c76f 0xc00003c76d 0xc00003c76b 0xc00003c769 0xc00003c767
	//0xc00003c76e 0xc00003c76c 0xc00003c76a 0xc00003c768 0xc00003c766
	foo1()

	// 输出,此处foo1函数内联, foo2非内联 所以main和foo定义的变量地址是交替的, foo2的地址是连续的
	//0xc00003c76f 0xc00003c76d 0xc00003c76b 0xc00003c769 0xc00003c767
	//0xc00003c76e 0xc00003c76c 0xc00003c76a 0xc00003c768 0xc00003c766
	// foo2 地址是连续的
	//0xc00003c73f 0xc00003c73e 0xc00003c73d 0xc00003c73c 0xc00003c73b
	foo2()

	// 输出,此处foo1函数内联, foo2非内联 所以main和foo定义的变量地址是交替的, foo2的地址是连续的
	//0xc00003c76e 0xc00003c76c 0xc00003c768 0xc00003c765 0xc00003c762
	//0xc00003c76d 0xc00003c76a 0xc00003c767 0xc00003c766 0xc00003c763
	//0xc00003c73f 0xc00003c73e 0xc00003c73d 0xc00003c73c 0xc00003c73b
	// foo3 是内联的, 所以不存在内存逃逸情况, 所以地址的连续的
	//0xc00003c76f 0xc00003c76b 0xc00003c769 0xc00003c764 0xc00003c761
	foo3()

	// 输出,此处foo1函数内联, foo2非内联 所以main和foo定义的变量地址是交替的, foo2的地址是连续的
	//0xc00003c76e 0xc00003c76c 0xc00003c768 0xc00003c765 0xc00003c762
	//0xc00003c76d 0xc00003c76a 0xc00003c767 0xc00003c766 0xc00003c763
	//0xc00003c73f 0xc00003c73e 0xc00003c73d 0xc00003c73c 0xc00003c73b
	//0xc00003c76f 0xc00003c76b 0xc00003c769 0xc00003c764 0xc00003c761
	// foo4 非inline, 且var3是内存逃逸, 所以地址是在堆上分配, 其他地址都是栈分配(内存地址连续)
	//0xc00003c737 0xc00003c736 0xc00001c088 0xc00003c735 0xc00003c734
	foo4()

	// foo5 测试new 生成的实例内存情况
	//0xc00003c76e 0xc00003c76c 0xc00003c768 0xc00003c765 0xc00003c762
	//0xc00003c76d 0xc00003c76a 0xc00003c767 0xc00003c766 0xc00003c763
	//0xc00003c73f 0xc00003c73e 0xc00003c73d 0xc00003c73c 0xc00003c73b
	//0xc00003c76f 0xc00003c76b 0xc00003c769 0xc00003c764 0xc00003c761
	//0xc00003c737 0xc00003c736 0xc00001c088 0xc00003c735 0xc00003c734
	//0xc00003c76f 0xc00003c76e 0xc00003c76d 0xc00003c76c 0xc00003c76b
	foo5()

	// foo6 测试new 生成的实例内存情况
	//0xc00003c768 0xc00003c767 0xc00003c764 0xc00003c760 0xc00003c75e
	//0xc00003c769 0xc00003c765 0xc00003c763 0xc00003c75f 0xc00003c75c
	//0xc00003c737 0xc00003c736 0xc00003c735 0xc00003c734 0xc00003c733
	//0xc00003c76a 0xc00003c766 0xc00003c762 0xc00003c761 0xc00003c75d
	// foo4地址
	//0xc00003c72f 0xc00003c72e 0xc00001c088 0xc00003c72d 0xc00003c72c
	//0xc00003c76f 0xc00003c76e 0xc00003c76d 0xc00003c76c 0xc00003c76b
	// foo6地址, 除了var3,其他变量和foo4地址公用
	//0xc00003c72e 0xc00003c72d 0xc00001c089 0xc00003c72c 0xc00003c72f
	foo6()
}

// 测试inline
func foo1() {
    
    
	var val1 int8 = 1
	var val2 int8 = 2
	var val3 int8 = 3
	var val4 int8 = 4
	var val5 int8 = 5
	println(&val1, &val2, &val3, &val4, &val5, "foo1(inline)")
}

// 测试非inline
func foo2() {
    
    
	var val1 int8 = 1
	var val2 int8 = 2
	var val3 int8 = 3
	var val4 int8 = 4
	var val5 int8 = 5
	for i := 0; i < 1; i++ {
    
    
		println(&val1, &val2, &val3, &val4, &val5, "foo2(none inline)")
	}
}

// 测试inline, 且内存逃逸
func foo3() *int8 {
    
    
	var val1 int8 = 1
	var val2 int8 = 2
	var val3 int8 = 3
	var val4 int8 = 4
	var val5 int8 = 5
	println(&val1, &val2, &val3, &val4, &val5, "foo3(inline) var3 excape")
	return &val3
}

// 测试非inline, 且内存逃逸
func foo4() *int8 {
    
    
	var val1 int8 = 1
	var val2 int8 = 2
	var val3 int8 = 3
	var val4 int8 = 4
	var val5 int8 = 5
	for i := 0; i < 1; i++ {
    
    
		println(&val1, &val2, &val3, &val4, &val5, "foo4(none inline) var3 excape")
	}
	return &val3
}

// 测试inline new实例变量
func foo5() *int8 {
    
    
	var val1 *int8 = new(int8)
	var val2 *int8 = new(int8)
	var val3 *int8 = new(int8)
	var val4 *int8 = new(int8)
	var val5 *int8 = new(int8)
	println(val1, val2, val3, val4, val5, "foo5(inline) var3 excape")
	return val3
}

// 测试非inline new实例变量(和其他变量公用内存, 如果使用任何变量,则地址不共用,此处var5不共用)
func foo6() *int8 {
    
    
	var val1 *int8 = new(int8)
	var val2 *int8 = new(int8)
	var val3 *int8 = new(int8)
	var val4 *int8 = new(int8)
	var val5 *int8 = new(int8)
	*val5 = 12
	for i := 0; i < 1; i++ {
    
    
		// fmt.Println(*val5)						// 取消注释, 则var地址与foo4不共用
		println(val1, val2, val3, val4, val5, "foo6(none inline) var3 excape")
	}
	return val3
}

结论

  1. 对于inline的函数, 其内部变量和main函数内部变量内存地址是连续的,共用main函数栈空间,此外,因为是inline, 所以不存在逃逸变量,所以var3地址和其他变量地址连续的,如图:
    在这里插入图片描述

  2. 对于非inline函数,其变量地址在非main的栈空间连续, 此外, 逃逸变量会在堆上分配内存,如下图中的(0xc00008e000, 0xc00008e001), 如下图:
    在这里插入图片描述

  3. 上图中foo5和foo6是new函数实例化的变量,和正常实例化变量无显著区别, 其内存分配情况还是要看是否变量地址逃逸

  4. go tool compile -m tests/stack_analysis.go 结果如图(foo4和foo6 var3变量为逃逸变量, 对内存分配, foo3和foo5因inline, compile为escape, 实际地址还是栈分配的):
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ERIC_TWELL/article/details/122047126