Go教程

【金秋打卡】第2天 GO 的堆内存结构和mspan

本文主要是介绍【金秋打卡】第2天 GO 的堆内存结构和mspan,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

课程名称:深入Go底层原理,重写Redis中间件实战

课程章节:9-3,9-4,9-5

课程讲师:Moody

课程内容

★heapArena

  • Go每次申请的虚拟单元为64MB

  • 最多有2的22次方 4,194,304 个虚拟内存单元

  • 内存单元也叫heapArena

  • 所有的heapArena组成了mheap(GO 堆内存)

32位系统给每个进程分配的是4g内存,而64位系统分配的是256T的虚拟内存

https://img3.sycdn.imooc.com/63580f510001073915440350.jpg

 ★如何使用heapArena

  • 线性分配

    线性分配器是一种高效的内存分配方式,只需要在内存中维护一个只想内存特定位置的指针,如果用户要内存,就检查剩余空闲内存,返回分配的内存区域并修改指针在内存中的位置。线性内存分配器有较低的实现复杂度,执行速度也很快。但是他无法利用已经分配过的内存。比如在整个“线”上,之前被分配的内存被GC回收释放,但是指针无法返回到被释放的内存位置,导致产生大量的内存碎片。


        线性分配器的内存碎片是通过定期合并空闲内存来整理的,一定程度上还增加了复杂度

  • 空闲链表分配

    空闲链表分配器在线性分配的基础上, 依次遍历空闲内存块,并使用链表存储这些空闲内存的指针。再次使用空闲内存的时候,会遍历这个链表,时间复杂度是O(n)。空闲链表分配器可以选择不同的策略在链表中进行选择:

    ▲首次适应(first-fit)----从链表头部开始遍历,选择第一个空间大于申请内存的内存块

    ▲循环首次适应(next-fit)----从上次遍历结束的位置开始遍历,选第一个空间大于申请内存的内存块

    ▲最优适应(best-fit)----从链表头部开始遍历整个链表,选择最合适的内存块

    ▲隔离适应(segregated-fit)----将内存分割成多个链表,每个链表中的内存大小相同,申请内存的时候,先找到满足条件的链表,再从链表中选择合适的内存块,这就非常接近Go的内存分配策略了

  • 分级分配

    线程缓存分配--TCMalloc,比glibc的malloc还要快一些。其核心理念是使用多级缓存将对象根据大小分类,并按照类别实施不同的分配策略。

 ★分级分配

  1. runtime.mspan 是go里面内存管理的基本单位,该结构体是可以组成一个链表-->runtime.mSpanList。

    next,prev分别指向前一个mspan和后一个mspanhttps://img2.sycdn.imooc.com/63590aa70001fb7806530153.jpg

  2. 每个runtime.mspan 都管理着npages 个大小为8kb的页,这些页不是系统的内存页,是go的内存页,大小为系统内存页的整倍数。

    ▲startAddr和npages ----确定该结构体管理的多个页所在内存,每个页的大小都是8k

    ▲freeindex ---- 扫描页中空闲对象的初始索引

    ▲allocBits 和 gcmarkBits ---- 分别用于内存占用(alloc)和 回收(gc)的情况

    ▲allocCache ---- allocBits 的不码,可用于快速查找内存中未被使用的内存https://img1.sycdn.imooc.com/63590abe0001e17705920699.jpg

  3. 运行时会使用runtime.mSpanStateBox 存储内存管理单元的状态 runtime.mSpanState

    https://img3.sycdn.imooc.com/635943e30001a04406820246.jpg

    状态共有四种mSpanDead、mSpanInUse、mSpanManual 和 mSpanFree,当mSpan处于空闲堆中,就是mSpanFree,当被分配后,状态是mSpanInUse或mSpanManual。运行的时候遵循以下状态:

    在GC任意阶段,可以从mSpanFree转换为mSpanInUse或mSpanManual

    ▲在GC清除阶段,可以从mSpanInUse或mSpanManual转换为mSpanFree

    ▲在GC标记阶段,不能从mSpanInUse或mSpanManual转换为mSpanFree

  4. 跨度Class

    runtime.spanClass是mspan的类型,mspan有67+1种类型,每一种类型都会存储特定大小的对象,并包含特定数量的页数。也就是说,mspan会根据需要,切割成67种不同的类型。从8B到32KB。还有一种0类型,主要是用于存储超过32kb的对象。https://img1.sycdn.imooc.com/635946b70001b3e306250194.jpg      

    spanClass除了存储类别ID,还会存储一个noscan的标志位,表示是否包含指针,GC会对包含指针的mspan进行扫描。

    spanClass是一个Unit8类型的整数,他的前7位是存储的跨度Class的类型ID,最后一位表示是否包含指针,通过位运算看最后一位是否为1,为1则认为是有指针

    https://img4.sycdn.imooc.com/63594ac50001b12106760131.jpg  

        这是什么意思呢,意思是需要gc扫描和不需要gc扫描,比如常量就不需要gc扫描

课程收获:

通过学习,明白了go内存分配的mspan的结构

https://img4.sycdn.imooc.com/6359649200015cf719241115.jpg




这篇关于【金秋打卡】第2天 GO 的堆内存结构和mspan的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!