Go 内存管理,内存分配

内存管理

内存管理是一个古老的话题,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运行时对堆内存对象管理做了三级划分,分别用mheap,mcentral,mcache表示不同的层级,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级别分配的总内存是81920,81920/27264 = 3,81920 % 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源码

你可能感兴趣的:(#,原理源码,分配内存)