go语言内存管理内存分配(代码片段)

ldaniel ldaniel     2022-10-22     661

关键词:

Go语言内存管理(一)内存分配

golang作为一种“高级语言”,也提供了自己的内存管理机制。这样一方面可以简化编码的流程,降低因内存使用导致出现问题的频率(C语言使用者尤其是初学者应该深有体会),对程序猿友好。另一方面也可以减少内存相关系统调用,提升性能。

先了解下内存管理大致策略:

  • 申请一块较大的地址空间(虚拟内存),用于内存分配及管理(golang:spans+bitmap+arena->512M+16G+512G)
  • 当空间不足时,向系统申请一块较大的内存,如100KB或者1MB
  • 申请到的内存块按特定的size,被分割成多种小块内存(golang:_NumSizeClasses = 67),并用链表管理起来
  • 创建对象时,按照对象大小,从空闲链表中查找到最适合的内存块
  • 销毁对象时,将对应的内存块返还空闲链表中以复用
  • 空闲内存达到阈值时,返还操作系统

以下,基于go1.9版本,看下golang内存分配实现的基本思路。

Go内存管理的实现

go的内存管理实现基于TCMalloc(Thread-Caching Malloc)。

TCMalloc是 Google 开发的多级内存分配器,具有对抗内存碎片化,适合高并发场景的特性。据称,它的内存分配速度是 glibc2.3 中实现的 malloc的数倍。

和TCMalloc相同,go的内存分配也是基于两种粒度的内存单位:span和object。span是连续的page,按page的数量进行归类,比如分为2个page的span,4个page的span等。object是span中按预设大小划分的块,也是按大小分类。同一个span中,只有一种类型(大小)的object。

go内存分配主要有三个管理组件:

  • mcache

Per-P(Processer,具体参见go中G,M,P的概念)私有cache,用于实现无锁的object分配

  • mcentral

全局内存,为各个cache提供按大小划分好的span

  • mheap

全局内存,page管理,内存不足时向系统申请

通过将内存分配流程分为三个层级,既能保证Processer级别(mcache)的无锁分配,又能在mcentral级别实现内存全局共享,避免浪费。

go将内存申请按大小分为三种类型:tiny,small,large。tiny是小于16个byte的申请,small是小于32KB的申请,大于32KB为large,三种类型的处理方式有所不同。

_TinySize      = 16
_MaxSmallSize   = 32768

我们以一个small对象为例,看一下内存申请流程:

  1. 计算对象大小,按预定义的sizeclass表(见下)从私有的mcache中找到对应规格的mspan。比如大小为112 byte的对象,对应8192 byte大小的mspan。然后通过mspan的空闲bitmap查找空闲的块,如果空闲块存在,分配完成。

以上是mcache内的分配操作,不需要加锁。

  1. 如果mspan没有空闲块,则向mcentral申请对应大小的空闲mspan。比如112 byte的对象,需要向mcentral申请8192 byte大小的空闲mspan。

由于申请获取全局的mspan,需要在mcentral级别加锁。

  1. 如果mcentral中没有空闲mspan,则向mheap申请,并划分object。

  2. 如果mheap没有足够的空闲page,则向操作系统申请不少于1M的page。

以上就是small对象的内存分配流程。

large对象的申请,跳过了mcache和mcentral,直接从mheap中分配。

对于tiny对象的申请,mcache中有专门的内存区域“tiny”来进行特殊处理。“tiny”将对象按大小与tinyoffset(“tiny”当前分配地址)对齐,然后分配,并记录下新的tinyoffset,用于下次分配。如果空间不足,则另外申请16 byte的内存块。

// sizeclass
// class  bytes/obj  bytes/span  objects  waste bytes
//     1          8        8192     1024            0
//     2         16        8192      512            0
//     3         32        8192      256            0
//     4         48        8192      170           32
//     5         64        8192      128            0
//     6         80        8192      102           32
//     7         96        8192       85           32
//     8        112        8192       73           16
//     9        128        8192       64            0
//    10        144        8192       56          128
//    11        160        8192       51           32
//    12        176        8192       46           96
//    13        192        8192       42          128
//    14        208        8192       39           80
//    15        224        8192       36          128
//    16        240        8192       34           32
//    17        256        8192       32            0
//    18        288        8192       28          128
//    19        320        8192       25          192
//    20        352        8192       23           96
//    21        384        8192       21          128
//    22        416        8192       19          288
//    23        448        8192       18          128
//    24        480        8192       17           32
//    25        512        8192       16            0
//    26        576        8192       14          128
//    27        640        8192       12          512
//    28        704        8192       11          448
//    29        768        8192       10          512
//    30        896        8192        9          128
//    31       1024        8192        8            0
//    32       1152        8192        7          128
//    33       1280        8192        6          512
//    34       1408       16384       11          896
//    35       1536        8192        5          512
//    36       1792       16384        9          256
//    37       2048        8192        4            0
//    38       2304       16384        7          256
//    39       2688        8192        3          128
//    40       3072       24576        8            0
//    41       3200       16384        5          384
//    42       3456       24576        7          384
//    43       4096        8192        2            0
//    44       4864       24576        5          256
//    45       5376       16384        3          256
//    46       6144       24576        4            0
//    47       6528       32768        5          128
//    48       6784       40960        6          256
//    49       6912       49152        7          768
//    50       8192        8192        1            0
//    51       9472       57344        6          512
//    52       9728       49152        5          512
//    53      10240       40960        4            0
//    54      10880       32768        3          128
//    55      12288       24576        2            0
//    56      13568       40960        3          256
//    57      14336       57344        4            0
//    58      16384       16384        1            0
//    59      18432       73728        4            0
//    60      19072       57344        3          128
//    61      20480       40960        2            0
//    62      21760       65536        3          256
//    63      24576       24576        1            0
//    64      27264       81920        3          128
//    65      28672       57344        2            0
//    66      32768       32768        1            0

参考文献:

TCMalloc

图解 TCMalloc

浅析go内存管理架构(代码片段)

...ge=8KB、本文的内存特指虚拟内存今天我们开始进入《Go语言轻松系列》第二章「内存与垃圾回收」第二部分「Go语言内存管理」。关于「内存与垃圾回收」章节,会从如下三大部分展开:读前知识储备(已完结)指针的大... 查看详情

go内存分配(代码片段)

...;4.2分配顺序1Go的内存分配核心思想Go是内置运行时的编程语言(runtime),像这种内置运行时的编程语言通常会抛弃传统的内存分配方式,改为自己管理。这样可以完成类似预分配、内存池等操作,以避开系统调用带来的... 查看详情

go内存分配(代码片段)

...;4.2分配顺序1Go的内存分配核心思想Go是内置运行时的编程语言(runtime),像这种内置运行时的编程语言通常会抛弃传统的内存分配方式,改为自己管理。这样可以完成类似预分配、内存池等操作,以避开系统调用带来的... 查看详情

go内存分配的学习(代码片段)

...上,这部分内存会由编译器进行管理;不同编程语言使用不同的方法管理堆区的内存,C++等 查看详情

go的内存管理(最新学习)(代码片段)

标题Go的内存管理内存管理的设计内存空间有堆区和栈区。栈一般存储局部变量,方法有关的数据,由编译器自动管理,。堆用来存放对象,java和go都是通过垃圾收集器回收,不需要手动对内存进行释放和管理... 查看详情

图解golang的内存分配(代码片段)

...真实逻辑)图:Go的内存分配核心思想Go是内置运行时的编程语言(runtime),像这种内置运行时的编程语言通常会抛弃传统的内存分配方式,改为自己管理。这样可以完成类似预分配、内存池等操作,以避开系统调用带来的性能问题,... 查看详情

go的内存管理(最新学习)(代码片段)

标题Go的内存管理内存管理的设计内存空间有堆区和栈区。栈一般存储局部变量,方法有关的数据,由编译器自动管理,。堆用来存放对象,java和go都是通过垃圾收集器回收,不需要手动对内存进行释放和管理... 查看详情

go学习go内存管理与并发控制(代码片段)

目录内存管理内存分配原理1.前言2.基础概念2.1span2.2cache2.3central2.4heap3.内存分配过程4.总结垃圾回收原理1.前言2.垃圾回收算法3.Golang垃圾回收3.1垃圾回收原理3.2内存标记(Mark)3.3三色标记法3.4StopTheWorld4.垃圾回收优化4.1写屏障(WriteBar... 查看详情

go语言学习笔记—基础—基本语法—常量与变量—变量的生命周期:变量逃逸分析——go编译器自动决定变量的内存分配方式(堆还是栈),提高程序运行效率(代码片段)

C/C++语言需要开发者自己管理内存分配,选择合适的内存分配方式以适应不同算法需求:函数的局部变量尽量使用栈内存分配;全局变量、结构体成员变量使用堆内存分配等。变量逃逸分析:go语言把内存分... 查看详情

go要违背初心吗?新提案:手动管理内存(代码片段)

...一般提案中所提到的arena并不会在一门带垃圾回收的编程语言中实现。因为会操作到内存就有可能会不安全,不符合带垃圾回收的语言定义。该库底层采取了动态检查来确保arena释放内存的操作是安全的。若出现异常情况,就会... 查看详情

go语言内存分配学习笔记

Go语言运行时依靠细微的对象分割、极致的多级缓存、精准的位图管理实现了对内存的精细化管理。一、内存分配1、span与元素Go语言将内存分成了67个级别的span,其中,0级代表特殊的大对象,其大小是不固定的。当... 查看详情

go语言内存分配学习笔记

Go语言运行时依靠细微的对象分割、极致的多级缓存、精准的位图管理实现了对内存的精细化管理。一、内存分配1、span与元素Go语言将内存分成了67个级别的span,其中,0级代表特殊的大对象,其大小是不固定的。当... 查看详情

go语言的特性(代码片段)

一、golang语言特性 1.垃圾回收a、内存自动回收,再也不需要开发人员管理内存 //开发代码中不能存在无引用的变量,不然代码出错b、开发人员专注业务实现,降低了心智负担c、只需要new分配内存,不需要释放2.天然并发... 查看详情

go语言之go语言make和new(代码片段)

GO语言make和newGo语言中new和make是两个内置函数,主要用来创建并分配类型的内存。new只分配内存,而make只能用于slice、map和channel的初始化。new在Go语言中,new函数描述如下://Thenewbuilt-infunctionallocatesmemory.Thefirstargumentisatype,//notava... 查看详情

c语言之动态内存管理(动态内存分配+经典笔试题+柔性数组)[建议收藏](代码片段)

本篇文章我要给大家梳理一下C语言中的动态内存管理相关知识。其中主要包括如何进行动态内存管理、一些常见的动态内存错误及柔性数组的介绍。❤️博主码云gitee链接:https://gitee.com/byte-binxin❤️文章目录为什么存在动... 查看详情

内存管理(代码片段)

前言像C语言这样的底层语言一般都有底层的内存管理接口,比如malloc()和free()用于分配和释放内存。而对于JavaScript来说,会在创建变量时分配内存,并且在不再使用它们时自动释放内存,这个自动释放内存的过程称为垃圾回收... 查看详情

go程序开发快速入门(代码片段)

...量名和注释,确保代码易于理解和维护。2、错误处理:Go语言有很好的错误处理机制,应该合理地处理错误,以便于排除错误。3、内存管理:Go语言自动管理内存,但是如果存在大量的内存分配和垃圾回收,会影响程序性能,因... 查看详情

go语言内存管理(三):逃逸分析

参考技术AGo语言较之C语言一个很大的优势就是自带GC功能,可GC并不是没有代价的。写C语言的时候,在一个函数内声明的变量,在函数退出后会自动释放掉,因为这些变量分配在栈上。如果你期望变量的数据可以在函数退出后仍... 查看详情