Skip to content

在 Go 语言中,局部变量和全局变量的内存分配位置不一样,初始化与否也会影响落在哪一段。这里按类别整理:

程序的内存布局 ​

程序在运行时的内存布局通常包括以下几个部分:

  • 代码段(Text Segment):存储程序的可执行指令,是只读的。
  • 数据段(Data Segment):存储已初始化的全局变量和静态变量。
  • BSS 段(Block Started by Symbol):存储未初始化的全局变量和静态变量。
  • 堆(Heap):用于动态分配的内存,通常由程序显式分配和释放。
  • 栈(Stack):用于存储函数调用的上下文,包括局部变量、函数参数和返回地址等。

内存布局示意图 ​

程序的内存布局可以用以下示意图表示:

---------------------
|       栈           |
| (局部变量)          |
---------------------
|       堆           |
| (动态分配内存)       |
---------------------
|       BSS          |
| (未初始化全局变量)   |
---------------------
|       数据段        |
| (已初始化全局变量)    |
---------------------
|       代码段        |
| (程序指令)          |
---------------------

全局变量 ​

全局变量(包级变量)是在包级别声明的变量,它们的作用域为整个包,并且在程序启动时就分配内存。

初始化的全局变量 ​

初始化的全局变量在程序启动时分配内存,并存储在数据段(Data Segment)中。数据段是内存中专门用来存储程序的静态数据(包括全局变量、静态变量等)的区域。

例如:

go
package main

var initializedGlobalVar = 42

func main() {
    // ...
}

未初始化的全局变量 ​

未初始化的全局变量在程序启动时也会分配内存,但它们存储在 BSS 段(Block Started by Symbol),BSS 段专门用来存储程序中未显式初始化的全局变量。

例如:

go
package main

var uninitializedGlobalVar int

func main() {
    // ...
}

局部变量 ​

局部变量是在函数或代码块内部声明的变量,它们的作用域仅限于声明它们的函数或代码块。

初始化的局部变量 ​

初始化的局部变量在函数或代码块执行时分配内存,并存储在栈(Stack)中。栈内存用于存储函数调用过程中的临时数据,包括局部变量、函数参数等。栈内存具有快速分配和释放的特点。

例如:

go
func exampleFunction() {
    initializedLocalVar := 42
    _ = initializedLocalVar // 使用一下,避免编译报错
}

未初始化的局部变量 ​

未初始化的局部变量在函数或代码块执行时也会分配内存,并存储在栈中。Go 语言会自动为这些变量赋予零值。

例如:

go
func exampleFunction() {
    var uninitializedLocalVar int
    _ = uninitializedLocalVar // 使用一下,避免编译报错
}

代码示例 ​

四类变量放在一起看:

go
package main

import "fmt"

// 初始化的全局变量
var initializedGlobalVar = 42

// 未初始化的全局变量
var uninitializedGlobalVar int

func main() {
    // 初始化的局部变量
    initializedLocalVar := 10

    // 未初始化的局部变量
    var uninitializedLocalVar int

    fmt.Println("Initialized Global Variable:", initializedGlobalVar)
    fmt.Println("Uninitialized Global Variable:", uninitializedGlobalVar)
    fmt.Println("Initialized Local Variable:", initializedLocalVar)
    fmt.Println("Uninitialized Local Variable:", uninitializedLocalVar)
}

代码段(Code Segment) ​

代码段是内存中的一个区域,用于存储程序的机器码,即程序的可执行指令。代码段是只读的,以防止程序在运行时修改其自身的指令。全局变量和局部变量并不存储在代码段中,而是存储在数据段、BSS 段或栈中。

逃逸分析 ​

局部变量默认在栈上,但编译器会做逃逸分析(escape analysis):变量的地址被带出函数、被闭包捕获、或者体积过大时,它会被搬到堆上,生命周期改由 GC 管。

go
package main

import "fmt"

func main() {
    fmt.Println(foo())
}

func foo() *int {
    a := 42
    return &a // a 的地址逃出函数,a 被分配到堆上
}

判断某个变量到底落在栈还是堆,用 go build -gcflags=-m 看编译器的分析输出。

总结 ​

  • 全局变量:在程序启动时分配内存,已初始化的存储在数据段,未初始化的存储在 BSS 段。
  • 局部变量:在函数或代码块执行时分配内存,存储在栈中,不论是否初始化,未初始化的局部变量会被赋予零值。
  • 代码段:存储程序的机器码,是只读的。

变量声明位置和初始化与否,决定它落在数据段、BSS 段还是栈上;函数体内的变量跟着栈走,进出函数就分配回收,这也是局部变量生命周期短的原因。

参考资料 ​

同层页面 ​

相关页面 ​

Hello Golang