一.前言
笔者在经过了前期基础学习后,用go语言来实现自己面临的业务问题已经不再是问题,所以拥有了另一方面的求知欲--go语言自身的各种包,各种机制是如何实现的,本章主要在探究go语言的内存分配器,希望能用本文讲清楚go语言内存分配器的机制,帮助大家更好地理解go语言的运行机制。
二.简介
不同于c语言使用malloc和free来主动管理内存,golang让程序员避免了这一切繁杂的操作,它通过escape analysis来分配内存,通过garbage collection(gc)来回收内存,本文主要介绍内存分配部分的工作。
三.详细解释
3.1 golang内存分配时机
程序有两种内存,一种是堆栈(stack),一种是堆(heap),所有的堆内数据都被GC管理。
我们要明白什么时候程序会分配内存,在某些语言中是程序员主动申请的,在go语言中则依赖escape analysis,越多的值在堆栈,程序运行越快(存取速度比堆要快,仅次于直接位于CPU中的寄存器),以下是内存分配的一些时机
1. goloang只会把函数中确定不在函数结束后使用的变量放到堆栈,否则就会放到堆:一个值可能在构造该值的函数之后被引用-->变量上传
package main
func main(){
n:=answer()
println(*n/2)
}
func answer() *int{
x:=42
return &x
}
使用命令 go build -gcflags="-m -l"得到结果
./main.go:10:2: moved to heap: x
2.编译器确定值太大而无法放入堆栈
3.编译器在编译的时候无法得知这个值的具体大小
ps:将变量下传,变量还会留在堆栈中
type Reader interface{
Read(p []byte) (n int,err error)
}
//better than
type Reader interface{
Read(n int) (b []byte,err error)
}
//因为从上面传参数下去用的是堆栈,从下面往上传,则会escape到堆,导致程序更慢
3.2 golang内存分配方式
3.2.1 TCMalloc
学习go语言的内存分配方式之前,我们先来看看另一个内存分配器-->TCMalloc,全称Thread-Caching Malloc
。
TCMalloc有两个重要组成部分:线程内存(thread cache)和页堆(page heap)
3.2.1.1 线程内存
每一个内存页都被分为多个固定分配大小规格的空闲列表(free list
) 用于减少碎片化。这样每一个线程都可以获得一个用于无锁分配小对象的缓存,这样可以让并行程序分配小对象(<=32KB)非常高效。
如图所示,第一行就是长度为8字节的内存块,在threa