Go 内存管理,内存分配

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

内存管理

内存管理是一个古老的话题C/C++这类语言需手动管理堆内存的申请与释放。Go、Java这类带有垃圾回收器GC的语言堆内存的申请与释放可以交给其运行时来完成。Rust这种新兴语言通过编译器确定内存管理分配与回收方式其不需要手动管理内存也不需要垃圾回收器它是将对象的生命周期限定在作用域内对象生命周期超出作用域自动执行Drop方法来销毁对象这是编译器指定的行为。

Go内存管理

Go语言的内存管理可以分为四个阶段分别是

  1. 向操作系统申请内存由(Page Allocator)完成
  2. runtime为程序分配内存由(Object Allocator)完成
  3. runtime为程序做垃圾回收由(Garbage Collector)完成
  4. 向操作系统归还内存由(Scavenger)完成

向操作系统申请内存和归还内存调用其syscall即可。操作系统会为应用程序做虚拟内存与物理内存的映射之后返回虚拟内存的空间。

堆与栈

Go语言淡化了堆与栈的概念用户无法直接操作Go运行时的堆栈。Go程序的中操作系统为进程划分的栈空间为Go runtime所用同时堆空间被分为了两部分即runtime所使用的堆和Go 用户态代码所使用的堆同时goroutine的栈也是从Go用户代码所使用的堆中进行分配的这使得goroutine的栈可以"任性"扩容或缩容。下图描述了其大概原理。goroutine所使用的堆栈和传统的堆栈使用方法类似栈上的空间会随着方法的执行结束而被回收堆上的空间多数情况下由GC代为管理但Go 1.20之后加入的Arena特性可以将"部分堆空间"的管理权限交给用户这也是Go内存管理方式的改进。

os stack
-------------------
|                 |
|     runtime     |
|                 |
-------------------
         |
         |
 os heap |
------------------------------------------ 
|                 |                      |
|                 |      Go heap         |              -------------
|     runtime     |                      |              | stack     |
|                 |      user code       |              | goroutine |
|                 |                      |------------->-------------
------------------------------------------

runtime 所占的堆空间可以称之为堆外内存而用户代码持有的堆空间可以称之为Go堆。

三级内存对象管理

Go运行时对堆内存对象管理做了三级划分分别用mheapmcentralmcache表示不同的层级mheap是保存所有内存对象的结构mheap中有一个central字段其类型如下:

central [numSpanClasses]struct {
	mcentral mcentral
	pad      [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte
}

可见为数组类型数组的长度为nSpanClasses该值在Go 1.19.3版本中为68 << 1 = 136。central数组根据下标划分保存了不同级别的central不同级别的central中保存了该级别的mspan链表mspan是Go管理堆内存的基本单元其代表被分配的内存(相连的页)。mspan级别被分为67级以下是Go1.19.3源码中的注释位于src/runtime/sizeclasses.go文件中

// 级别   单个内存占用 总内存占用 分配对象个数  浪费字节数 最大浪费率  最小对齐方式  
// class  bytes/obj  bytes/span  objects  tail waste  max waste  min align
//     1          8        8192     1024           0     87.50%          8
//     2         16        8192      512           0     43.75%         16
//     3         24        8192      341           8     29.24%          8
//     4         32        8192      256           0     21.88%         32
//     5         48        8192      170          32     31.52%         16
//     6         64        8192      128           0     23.44%         64
//     7         80        8192      102          32     19.07%         16
//     8         96        8192       85          32     15.95%         32
//     9        112        8192       73          16     13.56%         16
//    10        128        8192       64           0     11.72%        128
//    11        144        8192       56         128     11.82%         16
//    12        160        8192       51          32      9.73%         32
//    13        176        8192       46          96      9.59%         16
//    14        192        8192       42         128      9.25%         64
//    15        208        8192       39          80      8.12%         16
//    16        224        8192       36         128      8.15%         32
//    17        240        8192       34          32      6.62%         16
//    18        256        8192       32           0      5.86%        256
//    19        288        8192       28         128     12.16%         32
//    20        320        8192       25         192     11.80%         64
//    21        352        8192       23          96      9.88%         32
//    22        384        8192       21         128      9.51%        128
//    23        416        8192       19         288     10.71%         32
//    24        448        8192       18         128      8.37%         64
//    25        480        8192       17          32      6.82%         32
//    26        512        8192       16           0      6.05%        512
//    27        576        8192       14         128     12.33%         64
//    28        640        8192       12         512     15.48%        128
//    29        704        8192       11         448     13.93%         64
//    30        768        8192       10         512     13.94%        256
//    31        896        8192        9         128     15.52%        128
//    32       1024        8192        8           0     12.40%       1024
//    33       1152        8192        7         128     12.41%        128
//    34       1280        8192        6         512     15.55%        256
//    35       1408       16384       11         896     14.00%        128
//    36       1536        8192        5         512     14.00%        512
//    37       1792       16384        9         256     15.57%        256
//    38       2048        8192        4           0     12.45%       2048
//    39       2304       16384        7         256     12.46%        256
//    40       2688        8192        3         128     15.59%        128
//    41       3072       24576        8           0     12.47%       1024
//    42       3200       16384        5         384      6.22%        128
//    43       3456       24576        7         384      8.83%        128
//    44       4096        8192        2           0     15.60%       4096
//    45       4864       24576        5         256     16.65%        256
//    46       5376       16384        3         256     10.92%        256
//    47       6144       24576        4           0     12.48%       2048
//    48       6528       32768        5         128      6.23%        128
//    49       6784       40960        6         256      4.36%        128
//    50       6912       49152        7         768      3.37%        256
//    51       8192        8192        1           0     15.61%       8192
//    52       9472       57344        6         512     14.28%        256
//    53       9728       49152        5         512      3.64%        512
//    54      10240       40960        4           0      4.99%       2048
//    55      10880       32768        3         128      6.24%        128
//    56      12288       24576        2           0     11.45%       4096
//    57      13568       40960        3         256      9.99%        256
//    58      14336       57344        4           0      5.35%       2048
//    59      16384       16384        1           0     12.49%       8192
//    60      18432       73728        4           0     11.11%       2048
//    61      19072       57344        3         128      3.57%        128
//    62      20480       40960        2           0      6.87%       4096
//    63      21760       65536        3         256      6.25%        256
//    64      24576       24576        1           0     11.45%       8192
//    65      27264       81920        3         128     10.00%        128
//    66      28672       57344        2           0      4.91%       4096
//    67      32768       32768        1           0     12.50%       8192

如下介绍浪费率的计算方式单个65级span占用字节数为27264为该span级别分配的总内存是8192081920/27264 = 381920 % 27264 = 128所以在分配三个span后还剩余128字节无法被分配回看单个64级span其占用字节数为24576多疑当需要一个24577字节内存的span时会分配65级的span。65级的span的浪费率的计算公式是((27264 - 24576 + 1) * 3 + 128/ 81920 = 0.999 ≈ 10 %。

在GMP调度模型中mcentral被所有的P共享同时P中有一个称之为mcache的span缓存。当P绑定的M执行G的时候需要使用内存则去mcache缓存中去获取若可以获取则拿来使用若无法获取则去mcentral中去寻找。mcache可以说是一个二级缓存。

Go内存分配策略

Go 内存分配策略可以分为顺序分配和自由表分配两种前者是连续的一片内存区域前边的内存区域是已被分配的内存后边的内存区域是未被分配的内存后者使用链表链接起来不同的未被分配的内存区域。前者对CPU的空间局部性十分友好但容易产生内存碎片不太便于管理。后者十分灵活。在Go语言中用自由表分配策略维护堆外空间的内存分配用顺序分配维护Go堆的内存分配用户侧代码。

总结

Go内存分配部分演进的比较挫折早期Go版本这方面逻辑非常粗糙。后期得以改进。在内存管理中如果需要超大的内存空间则有mheap单独向操作系统申请mheap中维护了代表内存使用状态的位图使用radix tree管理线性的地址空间。

Reference
《Go 语言底层原理剖析》
《Go 程序员面试笔试宝典》
Go 1.19.3 runtime源码

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go