Go 语言内存管理 | 青训营笔记

120 阅读3分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 4 天

本篇文章内容:

  • 自动内存管理
  • Go 内存管理及优化
  • 编译器和静态分析
  • Go 编译器优化

一、自动内存管理

01.自动内存管理

  • 动态内存

    • 程序在运行时根据需求动态分配内存:malloc()
  • 自动内存管理(垃圾回收):由程序语言的运行时系统管理动态内存

    • 避免手动内存管理,专注于实现业务逻辑
    • 保证内存使用的正确性安全性:double-free problem,use-after-free problem
  • 三个任务

    • 为新对象分配空间

    • 找到存活对象

    • 回收死亡对象的内存空间

  • Mutator: 业务线程,分配新对象,修改对象指向关系

  • Collector: GC 线程,找到存活对象,回收死亡对象的内存空间

  • Serial GC: 只有一个 collector

  • Parallel GC: 支持多个 collectors 同时回收的 GC 算法

  • Concurrent GC: mutator(s) 和 collector(s) 可以同时执行

    • Collectors:必须感知对象指向关系的改变!
  • 评价 GC 算法

    • 安全性(Safety):不能回收存辉的对象 基本要求

    • 吞吐率(Throughput): 1 - 程序执行总时间​ 花在 GC 上的时间

    • 暂停时间(Pause time):stop the world(STW)业务是否感知

    • 内存开销(Span overhead) GC 元数据开销

一、手动内存管理

Go语言采用了垃圾回收机制来管理内存。Go程序中的内存分配和回收由Go运行时管理。Go使用了复制垃圾回收算法来管理内存。当程序运行时,Go运行时会定期地暂停程序执行,检查程序中未使用的对象,并回收它们占用的内存。

Go语言还支持手动内存管理。可以使用内置的new和make函数来分配内存,并使用内置的copy和append函数来操作内存。 Go语言支持两种内存分配方式:

  1. 使用内置的new函数来分配内存。new函数分配内存并返回指向新分配内存的指针。
package main

import "fmt"

func main() {
    //使用new函数分配内存
    p := new(int)
    fmt.Println(*p) //0
    *p = 2
    fmt.Println(*p) //2
}
  1. 使用内置的make函数来分配内存。make函数分配内存并返回类型的零值。
package main

import "fmt"

func main() {
    //使用make函数分配内存
    s := make([]int, 0)
    fmt.Println(s) //[]
    s = append(s, 1)
    fmt.Println(s) //[1]
}

Go语言还支持内存对齐,这有助于提高程序性能。Go运行时会自动对齐内存,并且不需要开发人员手动对齐内存。内存对齐是指在内存中对某一数据类型的存储地址进行调整,使得它的地址是某个特定值的倍数。这样做的目的是为了提高程序的性能,因为当内存地址对齐时,CPU可以更快地访问内存中的数据。当定义结构体时,可以使用内置的"align"关键字来指定结构体的对齐方式.


package main

import "fmt"

type MyStruct struct {
    x int64
    y int64
    _ [0]byte
    align [3]byte
}

func main() {
    ms := MyStruct{x: 1, y: 2}
    fmt.Println(ms)
}

这样可以指定结构体MyStruct的对齐方式。