go当中的内存管理

本文深入探讨Go语言的内存管理机制,包括mspan、mcache、mcentral和mheap的源码解析,阐述了从微对象到大对象的内存分配过程,涉及Tiny对象的对齐与分配、mcache与mcentral的角色,以及mheap如何管理内存,特别是基数树在内存分配中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.mspan源码

type mspan struct {
   
	//mspan是一个双向链表结构,所以包含next指针和prev指针,分别指向后继节点和前驱节点
	next *mspan     
	prev *mspan    
	// 好像是用来debug的,具体不是很清楚
	list *mSpanList 
	// mspan主要是用来管理heap当中的内存,其实质为heap当中一连串页组成的一个结构,所以,startAddr保存的是该mspan从哪个页开始管理
	startAddr uintptr 
	// mspan中保存的heap当中页的数量
	npages    uintptr 
	//暂时还不太理解
	manualFreeList gclinkptr
	// 空闲对象的下标
	freeindex uintptr
	//msapn当中包含的object数量,可以根据npages和elementSize推算出来
	nelems uintptr
	//位图,方便查看对象是否分配
	allocCache uint64
	//用来标记mspan当中elem的使用情况
	allocBits  *gcBits
	//用来标记maspan当中对象是否被标记,因为go用的是标记清除算法
	gcmarkBits *gcBits
	//当前GC阶段,具体数值可看源码
	sweepgen    uint32
	// for divide by elemsize - divMagic.mul
	divMul      uint16     
	// if non-0, elemsize is a power of 2, & this will get object allocation base
	baseMask    uint16     
	//已经分配的object数量
	allocCount  uint16     
	//当前mspan对象大小,划分依据
	spanclass   spanClass
	//当前msapn的状态,空闲、垃圾回收、正在使用  
	state       mSpanState
	//在分配前是否需要清零 
	needzero    uint8 
	// for divide by elemsize - divMagic.shift     
	divShift    uint8      
	// for divide by elemsize - divMagic.shift2
	divShift2   uint8     
	//这个msapn中是否需要释放内存
	scavenged   bool 
	//object对象大小      
	elemsize    uintptr    
	//这个msapn中的最后一个object
	limit       uintptr  
	// guards specials list  
	speciallock mutex      
	// linked list of special records sorted by offset.
	specials    *special   
}

综上所述,mspan是go当中内存管理的基本单元,是由一片连续的 8KB的页组成的大块内存,1.它包含有startAddr以及npages可以确定其管理范围;
2.包含spanclass和elemsize可以确定当中每一个object的大小;
3.包含freeindex可以确定空闲对象的下表;
4.包含allocBits和gcmarkBits可以用来确定mspan中的分配情况以及垃圾回收过程中的标记情况。
5.包含的scavenged和sweegen可以表示垃圾回收的阶段如:是否需要回收、正在回收、正准备使用等。

2.mcahe源码

Go 像 TCMalloc 一样为每一个逻辑处理器P 提供一个本地span缓存称作 mcache。如果 协程 需要内存可以直接从 mcache 中获取,由于在同一时间只有一个 协程运行在 逻辑处理器P上,所以中间不需要任何锁的参与。mcache 包含所有大小规格的 mspan 作为缓存,但是每种规格大小只包含一个。

type mcache struct {
   
	// The following members are accessed on every malloc,
	// so they are grouped here for better caching.
	// trigger heap sample after allocating this many bytes
	next_sample uintptr
	//堆分配的,可扫描的字节数
	local_scan  uintptr
	//tiny对象在堆中的起始地址
	tiny             uintptr
	//tiny对象在堆中的偏移地址
	tinyoffset       uintptr
	//分配tiny对象的数量
	local_tinyallocs uintptr // number of tiny allocs not counted in other stats
	// The rest is not accessed on every malloc.
	//每一个mcache中都包含了67*2种msapn,67种是msapn规定的大小,而在mcache中分成两组,一组包含指针,一组不包含指针,便于gc
	alloc [numSpanClasses]*mspan 
	//没太看懂
	stackcache [_NumStackOrders]stackfreelist
	//large object释放的内存大小
	local_largefree  uintptr
	//large object被释放的数量
	local_nlargefree uintptr                  
	//每种mspan被释放的数量
	local_nsmallfree [_NumSizeClasses]uintptr 
	//是否需要gc
	flushGen uint32
}

综上所述
1.mcache中包含了tiny和tinyoffset对象用来分配微对象(<16B)
2.mcache中包含了alloc对象,该对象是67种msapn的集合,可以用来分配小对象(<32KB)。
3.mcache无法分配大对象,由堆直接分配(>32KB)

3.mcentral源码

mcentral 对象收集所有给定规格大小的 span。每一个 mcentral 都包含两个 mspan 的链表:

做这种区主要是为了更快的分配span到mcache中。

除了级别0,每一个级别都会有一个mcentral,管理span列表。

nonempty mspanList – 有空闲对象的 span链表

empty mspanList – 没有空闲对象或 span 已经被 mcache 缓存的 span 链表
在这里插入图片描述

type mcentral struct {
   
	//因为mcentral是全局的变量,每个线程不够了都要向mcentral申请,所以要有锁
	lock      mutex
	//2*sizeclass,包含scan和nonscan两种
	spanclass spanClass
	//type mSpanList struct {
   
	//first *mspan // first span in list, or nil if none
	//last  *mspan // last span in list, or nil if none
	//}
	//mspanList结构如上所示,就是一个msapn的列表,包含头尾
	//nonempty是已分配对象的列表
	nonempty  mSpanList 
	//未分配mspan对象列表
	empty     mSpanList 
	//已分配对象的个数
	nmalloc uint64
}

思路:这里的申请感觉就像是从库存里拿

4.mheap源码

而所有级别的这些mcentral,其实是一个数组,由 mheap进行管理。
在这里插入图片描述
mheap的作用不只是管理central,另外大对象也会直接通过mheap进行分配。mheap实现了对于虚拟内存线性地址空间的精准管理,建立了span与具体线性地址空间的联系,保存了分配的位图信息,是管理内存的最核心单元。后面我们还会看到,堆区的内存还被分成了HeapArea大小进行管理。对heap进行的操作必须得全局加锁,而不管是mcache、mcentral都只能看做是某种形式的缓存。

在这里插入图片描述

type mheap struct {
   
	// lock must only be acquired on the system stack, otherwise a g
	// could self-deadlock if its stack grows with the lock held.
	lock      mutex
	//mtreap 好像是go当中的一种组织形式
	free      mTreap 
	//垃圾回收的状态信息,与mspan当中的sweegen类似
	sweepgen  uint32 
	// all spans are swept
	sweepdone uint32 
	// number of active sweepone calls
	sweepers  uint32 
	//所有mspan的存储
	allspans []*mspan 
	//两个栈,一个是回收后正在使用的,一个是还没回收正在使用的?还是一个是正在使用的,一个是还没划分的?
	sweepSpans [2]gcSweepBuf
	// align uint64 fields on 32-bit for atomics
	_ uint32 
	//正在使用的pages
	pagesInUse         uint64  
	//在本次gc种回收的page
	pagesSwept         uint64 
	//pagesSwept to use as the origin of the sweep ratio; updated atomically
	pagesSweptBasis    uint64
	// value of heap_live to use as the origin of sweep ratio; written with lock, read without
	sweepHeapLiveBasis uint64
	//proportional sweep ratio; written with lock, read without
	sweepPagesPerByte  float64 
	//下面的没看懂
	scavengeTimeBasis     int64
	scavengeRetainedBasis uint64
	scavengeBytesPerNS    float64
	scavengeRetainedGoal  uint64
	scavengeGen           uint64 // incremented on each pacing update

	reclaimIndex uint64

	reclaimCredit uintptr

	// Malloc stats.
	//大对象已经分配的比特数
	largealloc  uint64  
	//大对象已经分配的数量
	nlargealloc uint64   
	//大对象回收的比特数
	largefree   uint64    
	//大对象回收的数量
	nlargefree  uint64     
	//小对象回收的数量
	nsmallfree  [_NumSizeClasses]uint64 
	// arenas is the heap arena map. It points to the metadata for
	// the heap for every arena frame of the entire usable virtual
	// address space.
	arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena

	//预分配空间
	heapArenaAlloc linearAlloc

	// arenaHints is a list of addresses at which to attempt to
	// add more heap arenas. This is initially populated with a
	// set of general hint addresses, and grown with the bounds of
	// actual heap arena ranges.
	arenaHints 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值