Go 内存管理

查看原文

本文介绍了 Go 进程的内存模型。开篇从最简单的的 HTTP Server 实现开始讲起,介绍了两个概念:Virtual Memory Size(VSZ) / Resident Set Size(RSS),前者是虚拟内存,包含了换页出去的内存数据,后者是驻留在内存,不包含换页数据。我们知道内存在物理上可以等价为 8比特存储槽的数组,每个槽可以存一丢丢数据;而虚拟内存机制让每个程序都可以以为自己独占了所有内存,小 trick 是其实大家都在共享这有限的资源,但是操作系统会轮着把不频繁使用的页暂时丢回硬盘(换页) - 这一套机制可以通过 MMU,Page Table,PTE,TLB 等模块完成,应用程序无需担心。 在进程实际运行的时候,内存的划分会变成有固定的代码段,数据段,初始数据以及向下生长的 stack,向上生长的 heap - 这也是由操作系统提供的(exec家族函数)。操作系统还提供 mmap, brk, sbrk, madvise 管理动态内存,C 程序员则通过 更上层的封装 malloc / free 来管理内存。 Go 通过 汇编或者 cgo 来调用这些系统调用,而不是 libc,所以它不提供 malloc, 而是直接向 OS 通过 mmap 要内存。Go 使用类似 TCMalloc: Thread-Caching Malloc 的方法管理内存。它比 libc malloc 更快效率更高,做法是使用 thread-local cache 存储预分配的内存对象。具体来说,给对象做大小分级,每个级别的管理都不太一样,几个核心关键词:mheap,mcache,mcentral,代码实现在 runtime.MemStats 中。