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内的分配操作,不需要加锁。

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

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

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

  4. 如果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语言内存管理(一)内存分配的更多相关文章

  1. SAP专家培训之Netweaver ABAP内存管理和内存调优最佳实践

    培训者:SAP成都研究院开发人员Jerry Wang 1. Understanding Memory Objects in ABAP Note1: DATA itab WITH HEADER LINE ...

  2. davlik虚拟机内存管理之一——内存分配

    转载自http://www.miui.com/thread-74715-1-1.html dalvik虚拟机是Google在Android平台上的Java虚拟机的实现,内存管理是dalvik虚拟机中的 ...

  3. STL内存管理器的分配策略

    STL提供了很多泛型容器,如vector,list和map.程序员在使用这些容器时只需关心何时往容器内塞对象,而不用关心如何管理内存,需要用多少内存,这些STL容器极大地方便了C++程序的编写.例如可 ...

  4. 浅谈C语言内存管理、内存泄露、堆栈

    1.内存分配区间:         对于一个C语言程序而言,内存空间主要由五个部分组成:代码段(.text).数据段(.data).静态区(.BSS).堆和栈组成.         BSS段:BSS段 ...

  5. JVM堆内存管理与自定义分配参数详解

    堆内存模型: 在Java中,堆被划分成两个不同的区域:新生代(Young),老年代(Old).而Permanent属于永久代(方法区),不属于堆内存.新生代又被分为了三个区域:Eden,from  s ...

  6. java的内存管理 对象的分配与释放

    分配 程序员通过new为每个对象申请内存空间(基本类型除外),所有对象都在堆中分配空间:释放:对象的释放是由垃圾回收机制决定和执行的. Java内存分为两种:栈内存和堆内存 (1)在函数中定义的基本类 ...

  7. Delphi的内存管理及内存泄露问题 FastMM4

    这几天因为一个程序长时间运行出现比较严重的内存泄露问题,开始关注了一下内存管理方面的东西,以前也注意内存管理,创建了对象及时释放,但总有忘了处理的情况. 在Delphi中没有自动回收机制,所以一定要及 ...

  8. Java的内存管理与内存泄露

    作为Internet最流行的编程语言之一,Java现正非常流行.我们的网络应用程序就主要采用Java语言开发,大体上分为客户端.服务器和数据库三个层次.在进入测试过程中,我们发现有一个程序模块系统内存 ...

  9. Java内存管理-JVM内存模型以及JDK7和JDK8内存模型对比总结(三)

    勿在流沙住高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分享了JVM及其启动流程,今天介绍一下JVM内部的一些区域,以及具体的区域在运行 ...

  10. 【深入理解Java虚拟机】自动内存管理机制——内存区域划分

      Java与C++之间有一堵有内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来.C/C++程序员既拥有每一个对象的所有权,同时也担负着每一个对象生 ...

随机推荐

  1. GitHub For Beginners: Commit, Push And Go

    In Part 1 of this two-part GitHub tutorial, we examined the main uses for GitHub and bega5n the proc ...

  2. linux下磁盘占用达到100%了,找不到哪些大文件耗尽了磁盘

    Linux下的根分区使用率100%,但是查看/分区下的目录都不大,没有占用满,这该怎么处理? 重启是肯定有效的,目前处理情况:重新restart应用后,空间释放出来 1.lsof | grep del ...

  3. python函数式编程之装饰器(一)

    1.开放封闭原则 简单来说,就是对扩展开放,对修改封闭 在面向对象的编程方式中,经常会定义各种函数. 一个函数的使用分为定义阶段和使用阶段,一个函数定义完成以后,可能会在很多位置被调用 这意味着如果函 ...

  4. Nmap简单扫描

    Nmap所识别的6个端口状态. open(开放的) 应用程序正在该端口接收TCP 连接或者UDP报文.发现这一点常常是端口扫描 的主要目标.安全意识强的人们知道每个开放的端口 都是攻击的入口.攻击者或 ...

  5. java HotSpot 内存管理白皮书

    原文见:http://www.open-open.com/lib/view/open1381034220705.html.查阅资料后,对原文做了补充. 文中关于JVM的介绍基于JDK1.6的Hotsp ...

  6. RAID知识总结[转]

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://815632410.blog.51cto.com/1544685/1394306 ...

  7. bzoj 4827: [Hnoi2017]礼物 [fft]

    4827: [Hnoi2017]礼物 题意:略 以前做的了 化一化式子就是一个卷积和一些常数项 我记着确定调整值还要求一下导... #include <iostream> #include ...

  8. spring security 4 filter 顺序及作用

    Spring Security 有两个作用:认证和授权 一.Srping security 4 filter 别名及顺序 spring security 4 标准filter别名和顺序,因为经常要用就 ...

  9. XSD详解一 - 基本概念

    本分类下的文章主要是对W3School的文档进行整理:http://www.w3school.com.cn/x.asp XML Schema 是基于 XML 的 DTD 替代者. XML Schema ...

  10. 链表回文判断(C++)

    题目描述: 对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构. 给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构.保证链表长度小于等 ...