golang 学习笔记 ---内存分配与管理
Go语言——内存管理
参考:
问题
- 内存碎片:避免内存碎片,提高内存利用率。
- 多线程:稳定性,效率问题。
内存分配

- arena即为所谓的堆区,应用中需要的内存从这里分配, 大小为512G,为了方便管理把arena区域划分成一个个的page,每个page为8KB,一共有512GB/8KB个页
- spans区域存放span的指针,每个指针对应一个page,所以span区域的大小为(512GB/8KB) * 指针大小8byte = 512M
- bitmap区域大小也是通过arena计算出来512GB / (指针大小(8 byte) * 8 / 2) = 16G,用于表示arena区域中哪些地址保存了对象, 并且对象中哪些地址包含了指针,主要用于GC。
分配细节
- object size > 32K,则使用 mheap 直接分配。
- object size < 16 byte,不包含指针使用 mcache 的小对象分配器 tiny 直接分配;包含指针分配策略与[16 B, 32 K]类似。
- object size >= 16 byte && size <=32K byte 时,先使用 mcache 中对应的 size class 分配。
- 如果 mcache 对应的 size class 的 span 已经没有可用的块,则向 mcentral 请求。
- 如果 mcentral 也没有可用的块,则向 mheap 申请,并切分。
- 如果 mheap 也没有合适的 span,则向操作系统申请。
span
可以看出span是一个非常重要的数据结构,每个span包含若干个连续的page。
小对象分配会在span page中划分更小的粒度;大对象通过多页实现。
size class
go1.10\src\runtime\sizeclasses.go
// class  bytes/obj  bytes/span  objects  tail waste  max waste
//     1          8        8192     1024           0     87.50%
//     2         16        8192      512           0     43.75%
//     3         32        8192      256           0     46.88%
//     4         48        8192      170          32     31.52%
//     5         64        8192      128           0     23.44%
//     6         80        8192      102          32     19.07%
//     7         96        8192       85          32     15.95%
//     8        112        8192       73          16     13.56%
//     9        128        8192       64           0     11.72%
//    10        144        8192       56         128     11.82%
//    ...
//    65      28672       57344        2           0      4.91%
//    66      32768       32768        1           0     12.50%
上表中每列含义如下:
- class: class ID,每个span结构中都有一个class ID, 表示该span可处理的对象类型
- bytes/obj:该class代表对象的字节数
- bytes/span:每个span占用堆的字节数,也即页数*页大小
- objects: 每个span可分配的对象个数,也即(bytes/spans)/(bytes/obj)
- tail bytes: 每个span产生的内存碎片,也即(bytes/spans)%(bytes/obj)
上表可见最大的对象是32K大小,超过32K大小的由特殊的class表示,该class ID为0,每个class只包含一个对象。所以上面只有列出了1-66。
有点像装箱算法,按照规格分配,减少内存碎片。
struct
span是内存管理的基本单位,每个span用来管子特定的size class对象,根据size class,span将若干个页分成块进行管理。
go1.10\src\runtime\mheap.go
type mspan struct {
    next *mspan     // next span in list, or nil if none
    prev *mspan     // previous span in list, or nil if none
    startAddr uintptr // address of first byte of span aka s.base()
    npages    uintptr // number of pages in span
    nelems uintptr // number of object in the span.
    allocBits  *gcBits
    gcmarkBits *gcBits
    allocCount  uint16     // number of allocated objects
    spanclass   spanClass  // size class and noscan (uint8)
    elemsize    uintptr    // computed from sizeclass or from npages
}

以size class 10为例,npages=1,nelems=56,spanclass=10,elemsize=144;startAddr指arena区位置;next和prev指spans区,span链表;allocBits是一个bitmap,标记分配块分配情况,这个设计我也用过,之前用redis bitmap实现了IPAM。
cache
从上面我们知道go通过span来分配内存,那在哪里用span?通过之前的学习Go语言——goroutine并发模型,我们知道每个P都有mcache,通过mcache管理每个G需要的内存。
go1.10\src\runtime\mcache.go
type mcache struct {
   tiny             uintptr
   tinyoffset       uintptr
   alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass
}
numSpanClasses = _NumSizeClasses << 1
_NumSizeClasses = 67
alloc是span数组,长度是67 << 1,说明每种size class有2组元素。第一组span对象中包含了指针,叫做scan,表示需要gc scan;第二组没有指针,叫做noscan。提高gc scan性能。
mcache初始没有span,G先从central动态申请span,并缓存在cache。
central
go1.10\src\runtime\mcentral.go
type mcentral struct {
   lock      mutex
   spanclass spanClass
   nonempty  mSpanList // list of spans with a free object, ie a nonempty free list
   empty     mSpanList // list of spans with no free objects (or cached in an mcache)
   // nmalloc is the cumulative count of objects allocated from
   // this mcentral, assuming all spans in mcaches are
   // fully-allocated. Written atomically, read under STW.
   nmalloc uint64
}
- lock: 多个G并发从central申请span,所以需要lock,保证一致性
- spanclass : 每个mcentral管理着一组有相同size class的span列表
- nonempty: 指还有内存可用的span列表
- empty: 指没有内存可用的span列表
- nmalloc: 指累计分配的对象个数
线程从central获取span步骤如下:
- 加锁
- 从nonempty列表获取一个可用span,并将其从链表中删除
- 将取出的span放入empty链表
- 将span返回给线程
- 解锁
- 线程将该span缓存进cache
线程将span归还步骤如下:
- 加锁
- 将span从empty列表删除
- 将span加入nonempty列表
- 解锁
heap
central只管理特定的size class span,所以必然有一个更上层的数据结构,管理所有的sizeclass central,这就是heap。
go1.10\src\runtime\mheap.go
type mheap struct {
   lock      mutex
   spans []*mspan
   // Malloc stats.
   largealloc  uint64                  // bytes allocated for large objects
   nlargealloc uint64                  // number of large object allocations
   largefree   uint64                  // bytes freed for large objects (>maxsmallsize)
   nlargefree  uint64                  // number of frees for large objects (>maxsmallsize)
   // range of addresses we might see in the heap
   bitmap        uintptr // Points to one byte past the end of the bitmap
   bitmap_mapped uintptr
   arena_start uintptr
   arena_used  uintptr // Set with setArenaUsed.
   arena_alloc uintptr
   arena_end   uintptr
   arena_reserved bool
   central [numSpanClasses]struct {
      mcentral mcentral
      pad      [sys.CacheLineSize - unsafe.Sizeof(mcentral{})%sys.CacheLineSize]byte
   }
}
- spans:映射span -> page
- large:大对象,>32K
- bitmap: gc
- arena: arena区相关信息,pages,堆区
- central:通过size class管理span,每种size class对应两个central

golang 学习笔记 ---内存分配与管理的更多相关文章
- golang学习笔记6 beego项目路由设置
		golang学习笔记5 beego项目路由设置 前面我们已经创建了 beego 项目,而且我们也看到它已经运行起来了,那么是如何运行起来的呢?让我们从入口文件先分析起来吧: package main ... 
- go语言,golang学习笔记1 官网下载安装,中文社区,开发工具LiteIDE
		go语言,golang学习笔记1 官网下载安装,中文社区,开发工具LiteIDE Go语言是谷歌2009发布的专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速 ... 
- 《深入理解 Java 虚拟机》学习笔记 -- 内存区域
		<深入理解 Java 虚拟机>学习笔记 -- 内存区域 运行时数据区域 主要分为 6 部分: 程序计数器 虚拟机栈 本地方法栈 Java 堆 方法区 如图所示: 1. 程序计数器(线程私有 ... 
- JVM学习笔记——内存结构篇
		JVM学习笔记--内存结构篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的内存结构部分 我们会分为以下几部分进行介绍: JVM整体介绍 程序计数器 虚拟机栈 本地方法栈 堆 方法 ... 
- delphi 精要-读书笔记(内存分配释放)
		delphi 精要-读书笔记(内存分配释放) 1.内存分为三个区域:全局变量区,栈区,堆区 全局变量区:专门存放全局变量 栈区:分配在栈上的变量可被栈管理器自动释放 堆区:堆上的变量内存必须人 ... 
- golang学习笔记17 爬虫技术路线图,python,java,nodejs,go语言,scrapy主流框架介绍
		golang学习笔记17 爬虫技术路线图,python,java,nodejs,go语言,scrapy主流框架介绍 go语言爬虫框架:gocolly/colly,goquery,colly,chrom ... 
- golang学习笔记8 beego参数配置 打包linux命令
		golang学习笔记8 beego参数配置 打包linux命令 参数配置 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/docs/mvc/contro ... 
- golang学习笔记5 用bee工具创建项目 bee工具简介
		golang学习笔记5 用bee工具创建项目 bee工具简介 Bee 工具的使用 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/docs/instal ... 
- go语言,golang学习笔记2 web框架选择
		go语言,golang学习笔记2 web框架选择 用什么go web框架比较好呢?能不能推荐个中文资料多的web框架呢? beego框架用的人最多,中文资料最多 首页 - beego: 简约 & ... 
随机推荐
- GPUImage API 文档之GPUImagePicture类
			GPUImagePicture类静态图像处理操作,它可以是需要处理的静态图像,也可以是一张作为纹理使用的图片,调用向它发送processImage消息,进行图像滤镜处理. 方法 - (id)initW ... 
- RocketMQ【未完成】
			RocketMQ 是一款快速地.可靠地.分布式.容易使用的消息中间件,由 alibaba 开发,其前身是 metaq,metaq 的第一个版本是可以看成是linkedin的kafka(scala)的j ... 
- 加快AS的Gradle Build速度
			随着项目的代码以及依赖库的日益增多,构建的速度却是越来越慢,Android Studio2.0 之后提供了Instant Run貌似福利来了,但经过一段时间的使用发现不尽如人意,最大的吐槽点是首次编译 ... 
- linux系统安装apache服务器
			命令行下安装: sudo apt-get install apache2 安装完毕以后, 打开127.0.0.1,可以看到首页: 静态页面的路径是: /var/www/html 作者: NONO 出处 ... 
- 分布式系统介绍-PNUTS
			PNUTS是Yahoo!的分布式数据库系统,支持地域上分布的大规模并发操作.它根据主键的范围区间或者其哈希值的范围区间将表拆分为表单元(Tablet),多个表单元存储在一个服务器上.一个表单元控制器根 ... 
- 树莓派系统(Debain)中设置固定静态IP地址
			一.方法: 1.使用ssh登陆,使用以下命令设置静态ip. sudo nano /etc/dhcpcd.conf2.在dhcpcd.conf后添加以下内容(网上的方法): interface eth0 ... 
- 对IIC总线时序的一点理解以及ACK和NACK(NAK)
			参考自:http://blog.chinaunix.net/uid-16100003-id-3059814.html 关于IIC的响应问题:对于每一个接收设备(从设备,slaver),当它被寻址后,都 ... 
- PYQT操作JS并且截图事例
			如何安装PYQT,可以查看我的上一篇文章:http://www.cnblogs.com/liqiu/p/3361948.html 然后运行下面的带有JS程序的Python脚本即可: #-*- codi ... 
- vba 列转行
			Sub C2R() Dim RCount As Integer RCount = 2 Dim FillIn, FillIn2 Set FillIn = Worksheets("Fill-in ... 
- [转]一个简洁的 systemd 操作指南
			1.服务权限systemd有系统和用户区分:系统(/user/lib/systemd/system/).用户(/etc/lib/systemd/user/). 一般系统管理员手工创建的单元文件建议存放 ... 
