GO内存管理

GO内存管理

虚拟内存
  • 虚拟内存是操作系统的一大功能,它向进程屏蔽了底层的RAM和磁盘,并向进程提供了远超物理内存大小的内存空间,就是说所有进程使用的内存之和要远大于物理内存。
  • 在没有虚拟内存的时代,物理内存是被多个进程共享的,多进程同时访问同一个物理内存是存在并发访问的问题的,需要加锁操作。引入虚拟内存后,每个进程都有各自的虚拟内存,内存的并发访问问题的粒度从多进程级别降低到了多线程级别。
  • 进程访问内存,实际访问的是虚拟内存,虚拟内存通过页表查看,当前要访问的虚拟内存地址,是否已经加载到物理内存,如果已经加载到物理内存,则去物理内存数据,如果没有物理内存,则从磁盘加载数据到物理内存,并把物理内存地址和虚拟内存地址更新到页表。
堆和栈

进程管理虚拟内存,对虚拟内存进行了划分,其中堆和栈是虚拟内存上两块功能不同的内存区域,栈在高地址,从高地址向低地址增长。堆在低地址,从低地址向高地址增长。

  • 栈和堆相比有这么几个好处:
    1、栈的内存管理简单,分配比堆上快。
    2、栈的内存不需要回收,而堆需要,无论是主动free,还是被动的垃圾回收,这都需要花费额外的CPU。
    3 、栈上的内存有更好的局部性,堆上内存访问就不那么友好了,CPU访问的2块数据可能在不同的页上,CPU访问数据的时间可能就上去了。
内存块
  • 当我们说内存管理的时候,主要是指堆内存的管理,因为栈的内存管理不需要程序去操心,堆内存最初会是一个完整的大块,即未分配内存。当申请内存的时候,就会从未分配内存,分割出一个小内存块,然后用链表把所有的内存块链接起来。每个内存块都有一些基本信息,比如大小size,是否使用used,指向下一个内存块的指针next,实际数据存放位置data。
  • 一个内存块包含了3类信息, 元数据、用户数据和对齐字段,内存对齐是为了提高访问效率(比如内存块存储data后仅剩2Byge大小,进行内存对齐,会提高后续访问读取的效率)
  • 释放内存实质是把使用的内存块从链表中取出来,然后标记为未使用,当分配内存块的时候,可以从未使用内存块中有先查找大小相近的内存块,如果找不到,再从未分配的内存中分配内存。
  • 随着内存不断的申请和释放,内存上会存在大量的碎片,降低内存的使用率。为了解决内存碎片,可以将2个连续的未使用的内存块合并,减少碎片。
TCMalloc go内存管理的起源

TCMalloc是Thread Cache Malloc的简称,是Go内存管理的起源,Go的内存管理是借鉴了TCMalloc。在Linux里,其实有不少的内存管理库,比如glibc的ptmalloc,FreeBSD的jemalloc,Google的tcmalloc等等,为何会出现这么多的内存管理库?本质都是在多线程编程下,追求更高内存管理效率:更快的分配是主要目的。

同一进程内的所有线程是共享进程内存空间的,当多个线程同时访问内存时,就需要加锁,防止并发访问的问题,即多个线程操作同一块内存空间。

TCMalloc 面对这样的问题的做法,为每个线程预分配一块缓存,线程申请小内存,可以从缓存分配,这样多个线程申请内存时,从各自的缓存分配,无需加锁,提高了内存分配效率。但为线程预分配缓存需要一次系统调用,但后续线程申请小内存是都是在缓存分配,在用户态执行,没有系统调用,缩短了内存总体的分配和释放时间。

TCMalloc 重要概念
  • Page:操作系统对内存管理以页为单位,TCMalloc也是这样,只不过TCMalloc里的Page大小与操作系统里的大小并不一定相等,而是倍数关系。《TCMalloc解密》里称x64下Page大小是8KB。

  • Span:一组连续的Page被称为Span,比如可以有2个页大小的Span,也可以有16页大小的Span,Span比Page高一个层级,是为了方便管理一定大小的内存区域,Span是TCMalloc中内存管理的基本单位。

  • ThreadCache:每个线程各自的Cache,一个Cache包含多个空闲内存块链表,每个链表连接的都是内存块,同一个链表上内存块的大小是相同的,也可以说按内存块大小,给内存块分了个类,这样可以根据申请的内存大小,快速从合适的链表选择空闲内存块。由于每个线程有自己的ThreadCache,所以ThreadCache访问是无锁的。

  • CentralCache:是所有线程共享的缓存,也是保存的空闲内存块链表,链表的数量与ThreadCache中链表数量相同,当ThreadCache内存块不足时,可以从CentralCache取,当ThreadCache内存块多时,可以放回CentralCache。由于CentralCache是共享的,所以它的访问是要加锁的。

  • PageHeap:PageHeap是堆内存的抽象,PageHeap存的也是若干链表,链表保存的是Span,当CentralCache没有内存的时,会从PageHeap取,把1个Span拆成若干内存块,添加到对应大小的链表中,当CentralCache内存多的时候,会放回PageHeap。

go内存管理
  • 内存管理被设计为可以在并发环境快速执行,同时与垃圾收集器集成在了一起,在 Go 语言中有两种策略,一种用于较小的内存空间的分配,而另一种则用于较大的内存空间的分配。

  • 根据go的GMP模型,我们知道go程序在运行时,拥有GOMAXPROCS个处理器p,每个线程M被分配一个处理器P,并且一次最多运行一个goroutine,go中的每个p拥有一个mcache本地缓存,goroutine在申请内存是会从当前P的本地缓存mcache中获取,这种申请内存使用p的缓存,不需要加锁,所以效率很高。

go内存管理概念
  • Page。 于TCMalloc中的page相同,x64下一个page的大小是8kb
  • span。 于TCMalloc中的span相同,span是内存管理的基本单位,一组连续的page组成1个span
  • mcache。 与TCMalloc中的ThreadCache类似。mcache保存的是各种大小级别的span链表,相当于按内存块大小给内存块分了个类,小对象直接从mcache上分配,起到了缓存的作用,并且可以无锁访问。
  • mcentral。 与TCMalloc中的CentralCache类似,是所有线程共享的缓存,访问需要加锁。它按span class对span分类,串联成链表,当mcache的某个级别的span内存不够用时,它会向mcentral申请一个当前级别的span。mcentral和mcache结构一样,保存了各个大小级别不同的span链表。
  • mheap。与TCMalloc中的PageHeap类似,它是堆内存的抽象,把从OS申请出的内存页组成span,并保存起来。当mcentral的span不够用时,会向mheap申请,mheap不够用时向OS申请,向OS申请内存是按页来的,把从OS申请出的内存页组成span。
    mheap和PageHeap也有不同,mheap把span组织成树结构,而不是链表,并且是2颗树,然后把span分配到heapAreana进行管理,它包含地址映射和span是否包含指针等位图。
较小内存空间分配
  • mcache和P的绑定,完美解决了加锁问题,mcache和TCMalloc中的ThreadCache类似。
  • 对于小于 32kb 的,较小的内存空间的分配策略,Go 会从 mcache 的本地缓存中尝试获取内存。mcache持有一个被叫做 mspan 的内存块列表,列表的每一项都是一个span链表,span 的大小被划分为 8 字节大小到 32k 字节大小,约 70 个的大小等级,同一个链表里内存块的大小是相同的,每个等级的链表可以存储不同大小的对象。这样当分配内存时,我们根据对象的大小,找到相应的链表,在链表找到空闲的内存块即可。
  • 每个 span 链表会存在两份:一个链表用于不包含指针的对象,而另一个用于包含指针的对象(怎么区分一个对象是否包含指针,并不是在对象上进行标记,因为那样在会遍历标记每个对象,对对象做了更改,而是对对象所在的内存块采用位图进行标记,通过对内存块来标记达到目的)。这种区别使得垃圾收集器更加轻松,因为它不必扫描不包含任何指针的span链表。
较大内存空间分配
  • 对于超过 32kb 的分配,会向上取整到页的大小,并直接从堆上分配。
mcache相应span没有空间了怎么办

span内的所有内存块都被占用,没有剩余的空间继续分配对象,mcache会向mcentral申请一个span。mcentral和mcache一样,都是0-133这134个span class级别,但每个级别都保存了2个span list,即两个span链表。

mheap对span的管理

mheap里保存了2颗二叉排序树,free树和scav树,按span的page数量进行排序。free树保存的span是空闲并且非垃圾回收的span,scav树保存的是空闲并且已经垃圾回收的span。
如果是垃圾回收导致的span释放,span会被加入到scav树,否则加入到free树,

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值