内存的不同用途 根据不同的操作系统,一个进程可能被分配到不同的内存区域去执行。但是不管什么样的操作系统、什么样的计算机架构,进程使用的内存都可以按照功能大致分为以下4个部分:
函数的调用过程主要要点在于借助寄存器和内存帧栈传递参数和返回值。虽然同为编译型语言,Go 相较 C 对寄存器和栈的使用有一些差别,同时,Go 语言自带协程并引入 defer 等语句,在调用过程上显得更加复杂。 理解Go函数调用在CPU指令层的过程有助于编写高效的代码,在性能优化、Bug排查的时候,能更迅速的确定要点。本文以简短的示例代码和对应的汇编代码演示了Go的调用过程,展示了不同数据类型的参数的实际传递过程,同时分析了匿名函数、闭包作为参数或者返回值传递时,在内存上的实际数据结构。对于协程对栈的使用和实现细节,本文不展开。 术语 栈:每个进程/线程/goroutine有自己的调用栈,参数和返回值传递、函数的局部变量存放通常通过栈进行。和数据结构中的栈一样,内存栈也是后进先出,地址是从高地址向低地址生长。 栈帧:(stack frame)又常被称为帧(frame)。一个栈是由很多帧构成的,它描述了函数之间的调用关系。每一帧就对应了一次尚未返回的函数调用,帧本身也是以栈的形式存放数据的。 caller 调用者 callee 被调用者,如在 函数 A 里 调用 函数 B,A 是 caller,B 是 callee 寄存器(X86)
Clang是一个C语言、C++、Objective-C语言的轻量级编译器。源代码发布于BSD协议下。Clang将支持其普通lambda表达式、返回类型的简化处理以及更好的处理constexpr关键字。 http://clang.llvm.org/ GCC:GNU(Gnu’s Not Unix)编译器套装(GNU Compiler Collection,GCC),指一套编程语言编译器,以GPL及LGPL许可证所发行的自由软件,也是GNU项目的关键部分,也是GNU工具链的主要组成部分之一。GCC(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。1985年由理查德·马修·斯托曼开始发展,现在由自由软件基金会负责维护工作。GCC原本用C开发,后来因为LLVM、Clang的崛起,它更快地将开发语言转换为C++。
func add(a, b int) int { return a + b } 通过 go build -gcflags ‘-N -l’,我们禁用了编译优化,以使生成的汇编代码更加容易读懂。然后我们就可以用 go 工具 objdump -s main.add func (func是我们用的包名,也是 go build 生成的可执行文件的名称),将这个函数对应的汇编代码导出来。 main.go:20 0x22c0 48c744241800000000 MOVQ $0x0, 0x18(SP) main.go:21 0x22c9 488b442408 MOVQ 0x8(SP), AX main.go:21 0x22ce 488b4c2410 MOVQ 0x10(SP), CX main.go:21 0x22d3 4801c8 ADDQ CX, AX main.go:21 0x22d6 4889442418 MOVQ AX, 0x18(SP) main.go:21 0x22db c3 RET