go主要有以下五种内存存储区(c/c++也是一样):
Go的内存分配核心思想
Go是内置运行时的编程语言(runtime
),像这种内置运行时的编程语言通常会抛弃传统的内存分配方式,改为自己管理。这样可以完成类似预分配、内存池等操作,以避开系统调用带来的性能问题,防止每次分配内存都需要系统调用。
用户线程的调度以及生命周期管理都是用户层面,Go语言自己实现的,不借助OS系统调用,减少系统资源消耗。
Go的内存分配的核心思想可以分为以下几点:
- 每次从操作系统申请一大块儿的内存,由Go来对这块儿内存做分配,减少系统调用
- 内存分配算法采用Google的TCMalloc算法。算法比较复杂,究其原理可自行查阅。其核心思想就是把内存切分的非常的细小,分为多级管理,以降低锁的粒度。
- 回收对象内存时,并没有将其真正释放掉,只是放回预先分配的大块内存中,以便复用。只有内存闲置过多的时候,才会尝试归还部分内存给操作系统,降低整体开销
业界比较出名的内存分配器有Google的tcmalloc
和Facebook的jemalloc
。二者在避免内存碎片和性能上均比glic有比较大的优势,在多线程环境中效果更明显。
程序在内存中的分布
- 代码段(
.text
),也称文本段(Text Segment),存放着程序的机器码和只读数据,可执行指令就是从这里取得的。如果可能,系统会安排好相同程序的多个运行实体共享这些实例代码。这个段在内存中一般被标记为只读,任何对该区的写操作都会导致段错误(Segmentation Fault) - 数据段,包括已初始化的数据段(
.data
)和未初始化的数据段(.bss
),前者用来存放保存全局的和静态的已初始化变量,后者用来保存全局的和静态的未初始化变量。数据段在编译时分配 - 堆栈段分为堆和栈:
-
堆(
Heap
):用来存储程序运行时分配的变量。堆的大小并不固定,可动态扩张或缩减。其分配由malloc()、new()等这类实时内存分配函数来实现。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减) 堆的内存释放由应用程序去控制,通常一个new()就要对应一个delete(),如果程序员没有释放掉,那么在程序结束后操作系统会自动回收。 -
栈(
Stack
)是一种用来存储函数调用时的临时信息的结构,如函数调用所传递的参数、函数的返回地址、函数的局部变量等。 在程序运行时由编译器在需要的时候分配,在不需要的时候自动清除。栈的特性: 最后一个放入栈中的物体总是被最先拿出来,这个特性通常称为先进后出(FILO)队列。`
栈的基本操作: PUSH操作:向栈中添加数据,称为压栈,数据将放置在栈顶; POP操作:POP操作相反,在栈顶部移去一个元素,并将栈的大小减一,称为弹栈。
-
堆和栈的区别
-
分配和管理方式不同 :
- 堆是动态分配的,其空间的分配和释放都由程序员控制。
- 栈由编译器自动管理。栈有两种分配方式:静态分配和动态分配。
- 静态分配由编译器完成,比如局部变量的分配。
- 动态分配由alloca()函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无须手工控制。
-
产生碎片不同
- 对堆来说,频繁的new/delete或者malloc/free势必会造成内存空间的不连续,造成大量的碎片,使程序效率降低。
- 对栈而言,则不存在碎片问题,因为栈是先进后出的队列,永远不可能有一个内存块从栈中间弹出。
-
生长方向不同
- 堆是向着内存地址增加的方向增长的,从内存的低地址向高地址方向增长。
- 栈的生长方向与之相反,是向着内存地址减小的方向增长,由内存的高地址向低地址方向增长。
了解更多:
- 图解TCMalloc goole内存分配器
- C/C++程序的5种内存存储区及示意
参考文献:
- 程序在内存中的分布 https://www.cnblogs.com/Lynn-Zhang/p/5449199.html
- 从内存分配开始 https://mp.weixin.qq.com/s/EyWKFRu1xryoHY386QUcuA
- 译文:Go 内存分配器可视化指南 https://www.linuxzen.com/go-memory-allocator-visual-guide.html
- 图解Go语言内存分配 https://juejin.im/post/5c888a79e51d456ed11955a8
- Golang源码探索(三) GC的实现原理 https://www.cnblogs.com/zkweb/p/7880099.html
- 《Go专家编程》Go 内存管理 https://my.oschina.net/renhc/blog/2236782?spm=a2c4e.10696291.0.0.a8e219a4hLvsZx
- 雨痕<<Go源码解析>>
go内存分配(英文) https://andrestc.com/post/go-memory-allocation-pt1/