go 手动释放内存_golang 内存管理

news/2024/7/5 21:01:10

1、虚拟内存、物理内存和页表

b3b69684b88fe33f5fbf403992169739.png

每一个进程都有自身的虚拟内存,通过页表将虚拟内存映射到物理内存。虚拟内存空间被内核划分为了等长的部分,这部分称之为,物理内存也被划分为了同样大小的页,通常称之为页帧

fa6281983b5c2cd335bf06ecf7227354.png

不同的虚拟内存空间中的页可以映射到同一个物理内存页,内核可以决定哪些内存区域在进程间共享,哪些不共享;不是所有的虚拟空间页都会映射到物理内存,虚拟空间远大于物理内存。

用来将虚拟地址空间映射到物理地址空间的数据结构称为页表,实际中为了提高性能、节省空间往往采用多级页表,linux 采用了四级页表。

虚拟内存中的栈主要保存函数的参数、返回值和局部变量等,由编译器管理;用户主动申请的内存主要保存在堆中,由用户和编译器共同管理,并且堆中内存需要垃圾回收机制,java、go、python等语言本身会负责垃圾回收,c、c++等需要用户自己负责内存的申请和释放。

2、简单的内存分配方法

  1. 线性分配
    维护一个指向内存的指针,移动指针实现内存的分配

46e3b95553277378b9e6736f368fabf1.png


实现复杂度低,内存分配快,但需要不停拷贝、合并内存以实现内存释放和回收

  1. 链表分配器
    维护一个空闲内存的链表

d1c13448e24404e6910218048b9fbd64.png


实现复杂度低,内存的释放和回收简单,但是需要遍历链表,为了进一步提高效率,可以把内存分割成不同的大小,分别组成不同的链表,根据申请内存的大小使用相应的链表

3、TCMalloc

线程缓存分配(TCMalloc,thread-caching malloc)是一种快速、高效的内存分配方法,其核心思想是将内存划分为不同的级别,以减少锁竞争,TCMalloc 将内存划分为了 线程缓存(thread-cache)和页堆(page-heap)两个部分。

Thread Cache

每个线程都拥有一个线程缓存,不需要锁,线程缓存由不同的固定大小链表组成,根据对象的大小选择合适的链表分配内存,减少空间碎片化,小于 32K 的内存将在线程缓存分配。

d6c8a4277149436d0913568ed48f8ff5.png

Page Heap

对于超过 32K 的内存将由页堆直接分配,每个页堆包含不同大小的页集合,span 表示一组连续的页,包含页的起始地址(start)和页数(n_pages)。当页堆空间不足时将向操作系统申请更多的内存空间。

a1de6707b8aeeeb91d9624effab0505a.png

4、GO 内存分配

Go 的内存分配方式借鉴了 TCMalloc,根据对象的大小将对象分成了微对象、小对象和大对象三种:

| 类别 | 大小 |

| ------ | :---------- |

| 微对象 | (0, 16B) |

| 小对象 | [16B, 32KB] |

| 大对象 | (32KB, +∞) |

其中微对象使用微对象内存分配器,小对象使用线程缓存中心缓存,大对象使用页堆,具体,Go 将 32K 大小的内存对象划分为了 67 类,如下所示:

https://sourcegraph.com/github.com/golang/go@master/-/blob/src/runtime/sizeclasses.go#L83:1

 1// class 大小: 0 -> 32K
 2var class_to_size = [_NumSizeClasses]uint16{
 3    0, 8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 
 4    208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 
 5    576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792,
 6    2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 
 7    6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 
 8    14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768,
 9}
10
11// class 所需页数
12var class_to_allocnpages = [_NumSizeClasses]uint8{
13    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
14    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1,
15    3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 
16    7, 5, 8, 3, 10, 7, 4,
17}

例如,对于 1024B 的对象将使用 1 个页(8192B),则将页划分为了 8 个相同大小的区域(1024B):

dcb7e7f0aa9913410e0882efb56d8b70.png

mspan

mspan 是 go 内存管理的基本单元,也是组织 pages 的数据结构,多个 mspan 组成一个双向链表:

https://sourcegraph.com/github.com/golang/go@619072be4138e3fc092a9b77d57a9abc5333a4ab/-/blob/src/runtime/mheap.go#L402:6

 1type mspan struct {
 2    next *mspan     // next span in list, or nil if none
 3    prev *mspan     // previous span in list, or nil if none
 4
 5    startAddr uintptr // address of first byte of span aka s.base()
 6    npages    uintptr // number of pages in span
 7
 8    spanclass   spanClass     // size class and noscan (uint8)
 9
10    ...
11}

508135f4b851d12d013163ab05a234c8.png

mcache

mcache 是 go 的线程缓存,与逻辑处理器 P 绑定,每个 mcache 持有 67*2 个 mspan(scan + noscan)(保存在 alloc 中),用来存储微小对象:

https://sourcegraph.com/github.com/golang/go@619072be4138e3fc092a9b77d57a9abc5333a4ab/-/blob/src/runtime/mcache.go

1type mcache struct {
2    alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass
3
4    ...
5}

47731852a3f15bc17fffa494acb54317.png

当 mspan 空间不够时,mspan 会向 mcentral 申请更多的 mspans。

mcentral

mcentral 中心缓存是持有 mspans,包含两个链表:

  1. empty: 包含空闲对象的链表
  2. nonempty:不包含空闲对象的链表

访问中心缓存需要锁

https://sourcegraph.com/github.com/golang/go@619072be4138e3fc092a9b77d57a9abc5333a4ab/-/blob/src/runtime/mcentral.go

 1type mcentral struct {
 2    lock      mutex
 3    spanclass spanClass
 4
 5    // For !go115NewMCentralImpl.
 6    nonempty mSpanList // list of spans with a free object, ie a nonempty free list
 7    empty    mSpanList // list of spans with no free objects (or cached in an mcache)
 8
 9    ...
10}

2686c14abf11173cb66249ce6a693f46.png

当 mcentral 空间不足时,mcentral 会向 mheap 申请更多的页用来建立新的 mspan

mheap

mheap 是 go 中管理 heap 的全局唯一结构,它管理着虚拟内存空间

https://sourcegraph.com/github.com/golang/go@619072be4138e3fc092a9b77d57a9abc5333a4ab/-/blob/src/runtime/mheap.go#L73:2

 1type mheap struct {
 2    // lock must only be acquired on the system stack, otherwise a g
 3    // could self-deadlock if its stack grows with the lock held.
 4    lock      mutex
 5    pages     pageAlloc // page allocation data structure
 6
 7    // In general, this is a two-level mapping consisting of an L1
 8    // map and possibly many L2 maps. This saves space when there
 9    // are a huge number of arena frames. However, on many
10    // platforms (even 64-bit), arenaL1Bits is 0, making this
11    // effectively a single-level map. In this case, arenas[0]
12    // will never be nil.
13    arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena
14
15    // central free lists for small size classes.
16    // the padding makes sure that the mcentrals are
17    // spaced CacheLinePadSize bytes apart, so that each mcentral.lock
18    // gets its own cache line.
19    // central is indexed by spanClass.
20    central [numSpanClasses]struct {
21        mcentral mcentral
22        pad      [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte
23    }
24
25    ...
26}

mheap 中有两个重要的字段:

  1. central: 全局中心缓存,共 67 + 67 个
  2. arenas: 二维矩阵,heap 的内存区域,每个 areana 管理着一系列 8k pages

603b254b3d3cb5376d37c79fb28b065c.png

参考链接

  1. Go 内存分配器
  2. A visual guide to Go Memory Allocator from scratch (Golang)
  3. 深入Linux内核架构
  4. 虚拟内存

http://www.niftyadmin.cn/n/2498324.html

相关文章

python标准图示_3D图示Python标准自学教程基础篇(4)_文件处理

内容简介&#xff1a;本套课程为Python基础篇的第4套课程&#xff0c;主要讲解python文件处理的系统知识体系。首先讲解Python文件的概念以及常用读写函数open()、Read()、write()、close()以及文件读写访问标志。 其次讲解文件的典型应用文件的复制算法&#xff0c;以及with a…

java开发环境搭建-1

安卓开发所需软件&#xff1a; JDK Eclipse Android-Sdk ADT 其中jdk的下载和安装&#xff0c;详细见http://www.cnblogs.com/zhuxiaohui/p/3620685.html eclipse的下载地址为http://www.eclipse.org/downloads/&#xff0c;即选择当前版本【Eclipse IDE for Java EE Developer…

晚会现场屏幕控制软件_双11晚会呼声最高的主持人,不是汪涵也不是谢娜!

双11刚刚结束&#xff0c;很多人在双11前夕投入过多精力计算如何买最划算&#xff0c;以至于错过了双11晚会。今年双11晚会的阵容非常强大&#xff0c;而且天猫和苏宁易购都各自邀请嘉宾举办了为购物而生的晚会。如果你没看&#xff0c;没关系&#xff0c;网络上还可以重播&…

junit依赖_Java 单元测试Junit

一、 测试分类&#xff1a;1. 黑盒测试&#xff1a;不需要写代码&#xff0c;给输入值&#xff0c;看程序是否能够输出期望的值。2. 白盒测试&#xff1a;需要写代码的。关注程序具体的执行流程。二、 Junit使用&#xff1a;白盒测试* 步骤&#xff1a;1. 定义一个测试类(测试用…

python3安装_CentOS7安装Python3.x

CentOS(Community Enterprise Operating System&#xff0c;中文意思是社区企业操作系统)是Linux发行版之一&#xff0c;它是来自于Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成。CentOS 7 中默认安装了 Python&#xff0c;但是版本是2.x的&#xff0c;由…

ant vue 离线文档_github|一款基于vue+element-ui 的绝佳的通用型、中后台前端框架

vue-admin-beautiful&#xff0c;一款基于 vueelement-ui 的绝佳的通用型、中后台前端框架vue-admin-beautiful登录页面vue-admin-beautiful后台效果效果展示地址&#xff1a;http://beautiful.panm.cn/vue-admin-beautiful/github地址&#xff1a;https://github.com/chuzhixi…

图标和文字跟着div比例放大缩小_CAD常用比例教学:图纸比例、绘图比例、打印比例、注释性比例!...

CAD中涉及比例的概念有很多&#xff0c;有些概念比较好理解&#xff0c;例如图块的比例、填充的比例等&#xff0c;有些概念单独拿出来也不难理解&#xff0c;但几个概念混到一块&#xff0c;就容易糊涂了。之前发过不少关于比例的文章&#xff0c;还是有很多人经常问到比例的问…

如何检测被锁住的Oracle存储过程及处理办法汇总(转)

1.查看是哪一个存储过程被锁住查V$DB_OBJECT_CACHE视图select * from V$DB_OBJECT_CACHE where owner过程的所属用户 AND LOCKS!0 2.查看是哪一个sid&#xff0c;通过sid可以知道是哪一个session查v$access视图select * from v$access where owner过程的所属用户 and name刚才查…