前面几章内容
(一) go协程栈底层讲解
(二) go的堆内存结构分析
(三) 高级语言垃圾回收思路和如何减少性能影响原理分析
在go 中堆和栈都是用的堆内存
研究go 的堆内存的概念先弄清楚操作系统的虚拟内存
不是win 的虚拟内存(相当于linux 中的swap)指的是内存不够时候拿出来一块硬盘做虚拟内存
操作系统给应用提供的虚拟内存空间(在老的linux32位机器上,操作系统给每个进程一个4GB 的虚拟空间),因为操作系统不允许进程动物理内存的,因为物理内存是多个进程共用的(动了别人的内存,那么其他进程可能就崩溃了),所以说操作系统给每个进程提供的是虚拟内存,然后操作系统管理虚拟内存到物理内存之间的关系
虚拟内存背后是物理内存,也有可能有磁盘
Linux 获取虚拟内存: mmap、madvice
下面是以一台64位物理机,64GB内存,展示了 进程和物理内存之间隔着一个虚拟内存
有上图可以知道每个进程有独立的虚拟内存,那么现在在go程序中虚拟内存是怎么获取的?
是通过一个一个变量获取还是一批批获取的,答案是一批批获取的,在go中有个这样一个结构体 heapArena
mheap 与 heapArena 关系示意图:
注意下面的换算和细节:
2^20 * 64MB = 256 TB
heapArena 中相邻的内存块对应的虚拟内存不一定相邻
虚拟内存中相邻的对应的物理内存也不一定相邻
在go中虚拟内存是以heapArena 结构体方式 对应,那么每一块这样的内存在go中是如何使用的呢,这里有三种方式
线性分配或者链表分配容易出现空间碎片, 如图
分级分配的思想是,有如下几步
最后一栏是指最大浪费
比如说8字节的格子,最小放入1字节对象,就浪费了87%的空间
这个表中sizeclass.go
文件中可看到
每个heapArena 中的mspan 都不确定,那么我们该如何快速找到所需的mspan 级别呢?
这里如何快速找到就是内存管理单元需要做的事了,这里就用到了中心索引的方式
在每个协程中,有136个mcentral 结构体,其中68个组需要GC 扫描的mspan,68个组不需要GC 扫描的mspan
需要扫描和不需要扫描上面虚线部分,不是真正的内存,只是一个索引,它把下面的heapArena 开辟的内存一组一组索引起来,简单来说它就是下面heapArena的目录
线程缓存和架构之间的关系
工作原理就是:有一个中央的索引class0-67 虚线部分
但是有性能问题于是在线程中开辟了一个本地缓存索引
回顾上一节内容,看下图
他们之间的关系理解:
因为heapArena 中分了很多的小块,才有了中央索引;中央索引因为性能问题才有了本地缓存索引
go分配堆内存前,会按照对象的大小进行不同的分配,那么对象的大小是如何定义的呢,下面是针对对象大小的一个定义:
Tiny 微对象(0,16B)无指针
Small 小对象[16B, 32KB]
Large 大对象(32KB, +无穷大)
微小对象(32KB以下的)分配 到普通mspan (1-67级span)
大对象 量身定做mspan(0级span)(0级span 是没有固定大小的)
之前我们探讨的只有1-67级,但实际上总共有68级,那么0级主要作用是什么,其实0级有很大的作用,看下面:
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习