白话Go语言内存管理--内存分配原理

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现代高级编程语言管理内存的方式分为两种:自动和手动,像C、C++ 等编程语言使用手动管理内存的方式,工程师编写代码过程中需要主动申请或者释放内存;而 PHP、Java 和 Go 等语言使用自动的内存管理系统,有内存分配器和垃圾收集器来代为分配和回收内存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这篇文章会结合图示用每个人都能听懂的大白话解释清楚Go的内存分配原理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"关于Go的内存分配"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"语言里,从内存的分配到不再使用后内存的回收等等这些内存管理工作都是由"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"在底层完成的。虽然开发者在写代码时不必过度关心内存从分配到回收这个过程,但是"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"的内存分配策略里有不少有意思的设计,通过了解他们有助于我们自身的提高,也让我们能写出更高效的"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"程序。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Go内存管理的设计旨在在并发环境中快速运行,并与垃圾回收器集成在一起。让我们看一个简单的示例:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"package main\n\ntype smallStruct struct {\n a, b int64\n c, d float64\n}\n\nfunc main() {\n smallAllocation()\n}\n\n//go:noinline\nfunc smallAllocation() *smallStruct {\n return &smallStruct{}\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"函数上面的注释"},{"type":"codeinline","content":[{"type":"text","text":"//go:noinline"}]},{"type":"text","text":"将禁止"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"对该函数进行内联,这样"},{"type":"codeinline","content":[{"type":"text","text":"main"}]},{"type":"text","text":"函数就会使用"},{"type":"codeinline","content":[{"type":"text","text":"smallAllocation"}]},{"type":"text","text":"函数返回的指针变量,因为被多个函数使用,返回的这个变量将被分配到堆上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于内联的概念之前的文章有说过:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"内联是一种手动或编译器优化,用于将简短函数的调用替换为函数体本身。这么做的原因是它可以消除函数调用本身的开销,也使得编译器能更高效地执行其他的优化策略。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以如果上面的例子不干预编译器的话,编译器通过内联将"},{"type":"codeinline","content":[{"type":"text","text":"smallAllocation"}]},{"type":"text","text":"函数体里的内容直接放到"},{"type":"codeinline","content":[{"type":"text","text":"main"}]},{"type":"text","text":"函数里,这样就不会产生"},{"type":"codeinline","content":[{"type":"text","text":"smallAllocation"}]},{"type":"text","text":"这个函数的调用了,所有的变量都是"},{"type":"codeinline","content":[{"type":"text","text":"main"}]},{"type":"text","text":"函数内这个范围使用的,也就不在需要将变量往堆上分配了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"继续说上面那个例子,通过逃逸分析命令 "},{"type":"text","marks":[{"type":"strong"}],"text":"go tool compile -m main.go"},{"type":"text","text":" 可以确认我们上面的分析,"},{"type":"codeinline","content":[{"type":"text","text":"&smallStruct{}"}]},{"type":"text","text":"会被分配到堆上去。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"➜ go tool compile -m main.go\nmain.go:12:6: can inline main\nmain.go:10:9: &smallStruct literal escapes to heap\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"借助命令"},{"type":"text","marks":[{"type":"strong"}],"text":"go tool compile -S main.go"},{"type":"text","text":",可以显示该程序的汇编代码,也可以明确地向我们展示内存的分配:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"0x001d 00029 (main.go:10) LEAQ type.\"\".smallStruct(SB), AX\n0x0024 00036 (main.go:10) PCDATA $2, $0\n0x0024 00036 (main.go:10) MOVQ AX, (SP)\n0x0028 00040 (main.go:10) CALL runtime.newobject(SB)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"内置函数"},{"type":"codeinline","content":[{"type":"text","text":"newobject"}]},{"type":"text","text":"会通过调用另外一个内置函数"},{"type":"codeinline","content":[{"type":"text","text":"mallocgc"}]},{"type":"text","text":"在堆上分配新内存。在Go里面有两种内存分配策略,一种适用于程序里小内存块的申请,另一种适用于大内存块的申请,大内存块指的是大于32KB。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面我们来细聊一下这两种策略。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"小于32KB内存块的分配策略"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当程序里发生了"},{"type":"codeinline","content":[{"type":"text","text":"32kb"}]},{"type":"text","text":"以下的小块内存申请时,Go会从一个叫做的"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"的本地缓存给程序分配内存。这个本地缓存"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"持有一系列的大小为"},{"type":"codeinline","content":[{"type":"text","text":"32kb"}]},{"type":"text","text":"的内存块,这样的一个内存块里叫做"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":",它是要给程序分配内存时的分配单元。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9a/9ae47c3d72df87b6629a52bb3fd41978.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Go的调度器模型里,每个线程"},{"type":"codeinline","content":[{"type":"text","text":"M"}]},{"type":"text","text":"会绑定给一个处理器"},{"type":"codeinline","content":[{"type":"text","text":"P"}]},{"type":"text","text":",在单一粒度的时间里只能做多处理运行一个"},{"type":"codeinline","content":[{"type":"text","text":"goroutine"}]},{"type":"text","text":",每个"},{"type":"codeinline","content":[{"type":"text","text":"P"}]},{"type":"text","text":"都会绑定一个上面说的本地缓存"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"。当需要进行内存分配时,当前运行的"},{"type":"codeinline","content":[{"type":"text","text":"goroutine"}]},{"type":"text","text":"会从"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"中查找可用的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"。从本地"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"里分配内存时不需要加锁,这种分配策略效率更高。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那么有人就会问了,有的变量很小就是数字,有的却是一个复杂的结构体,申请内存时都分给他们一个"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"这样的单元会不会产生浪费。其实"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"持有的这一系列的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"并不都是统一大小的,而是按照大小,从8字节到32KB分了大概70类的"},{"type":"codeinline","content":[{"type":"text","text":"msapn"}]},{"type":"text","text":"。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/57/57c039112191ee76bd8ac57750635afa.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就文章开始的那个例子来说,那个结构体的大小是32字节,正好32字节的这种"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"能满足需求,那么分配内存的时候就会给它分配一个32字节大小的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/54/5471f3483cf69df56d89ac21c29105c9.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,我们可能会好奇,如果分配内存时"},{"type":"codeinline","content":[{"type":"text","text":"mcachce"}]},{"type":"text","text":"里没有空闲的32字节的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"了该怎么办?"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"里还为每种类别的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"维护着一个"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"的作用是为所有"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"提供切分好的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"资源。每个"},{"type":"codeinline","content":[{"type":"text","text":"central"}]},{"type":"text","text":"会持有一种特定大小的全局"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"列表,包括已分配出去的和未分配出去的。 每个"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"对应一种"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":",当工作线程的"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"中没有合适(也就是特定大小的)的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"时就会从"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":" 去获取。"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"被所有的工作线程共同享有,存在多个"},{"type":"codeinline","content":[{"type":"text","text":"goroutine"}]},{"type":"text","text":"竞争的情况,因此从"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"获取资源时需要加锁。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"的定义如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"//runtime/mcentral.go\n\ntype mcentral struct {\n // 互斥锁\n lock mutex \n \n // 规格\n sizeclass int32 \n \n // 尚有空闲object的mspan链表\n nonempty mSpanList \n \n // 没有空闲object的mspan链表,或者是已被mcache取走的msapn链表\n empty mSpanList \n \n // 已累计分配的对象个数\n nmalloc uint64 \n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"里维护着两个双向链表,"},{"type":"text","marks":[{"type":"strong"}],"text":"nonempty"},{"type":"text","text":"表示链表里还有空闲的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"待分配。**empty**表示这条链表里的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"都被分配了"},{"type":"codeinline","content":[{"type":"text","text":"object"}]},{"type":"text","text":"。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f5/f5110bef6f4597a437131121f65209f4.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果上面我们那个程序申请内存的时候,"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"里已经没有合适的空闲"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"了,那么工作线程就会像下图这样去"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"里去申请。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"简单说下"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"从"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"获取和归还"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"的流程:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"获取 加锁;从"},{"type":"codeinline","content":[{"type":"text","text":"nonempty"}]},{"type":"text","text":"链表找到一个可用的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":";并将其从"},{"type":"codeinline","content":[{"type":"text","text":"nonempty"}]},{"type":"text","text":"链表删除;将取出的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"加入到"},{"type":"codeinline","content":[{"type":"text","text":"empty"}]},{"type":"text","text":"链表;将"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"返回给工作线程;解锁。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"归还 加锁;将"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"从"},{"type":"codeinline","content":[{"type":"text","text":"empty"}]},{"type":"text","text":"链表删除;将"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"加入到"},{"type":"codeinline","content":[{"type":"text","text":"nonempty"}]},{"type":"text","text":"链表;解锁。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3d/3d1c2576f8fd97f2b14a25893d8d8d47.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"没有空闲的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"时,会向"},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"申请。而"},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"没有资源时,会向操作系统申请新内存。"},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"主要用于大对象的内存分配,以及管理未切割的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":",用于给"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"切割成小对象。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/65/6563ed7a81234f85316ac9465d5570e1.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同时我们也看到,"},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"中含有所有规格的"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":",所以,当一个"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"从"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"申请"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"时,只需要在独立的"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"中使用锁,并不会影响申请其他规格的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面说了每种尺寸的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"都有一个全局的列表存放在"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"里供所有线程使用,所有"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"的集合则是存放于"},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"中的。 "},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"里的"},{"type":"codeinline","content":[{"type":"text","text":"arena"}]},{"type":"text","text":" 区域是真正的堆区,运行时会将 "},{"type":"codeinline","content":[{"type":"text","text":"8KB"}]},{"type":"text","text":" 看做一页,这些内存页中存储了所有在堆上初始化的对象。运行时使用二维的 "},{"type":"link","attrs":{"href":"https://github.com/golang/go/blob/e7f9e17b7927cad7a93c5785e864799e8d9b4381/src/runtime/mheap.go#L217","title":""},"content":[{"type":"text","text":"runtime.heapArena"}]},{"type":"text","text":" 数组管理所有的内存,每个 "},{"type":"codeinline","content":[{"type":"text","text":"runtime.heapArena"}]},{"type":"text","text":" 都会管理 64MB 的内存。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ac/acf45ff994e25f720e5695c0116eb02c.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果 "},{"type":"codeinline","content":[{"type":"text","text":"arena"}]},{"type":"text","text":" 区域没有足够的空间,会调用 "},{"type":"link","attrs":{"href":"https://github.com/golang/go/blob/6dd11bcb35cba37f5994c1b9aaaf7d2dc13fd7cf/src/runtime/malloc.go#L617","title":""},"content":[{"type":"text","text":"runtime.mheap.sysAlloc"}]},{"type":"text","text":" 从操作系统中申请更多的内存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"大于32KB内存块的分配策略"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Go没法使用工作线程的本地缓存"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"和全局中心缓存"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"上管理超过32KB的内存分配,所以对于那些超过32KB的内存申请,会直接从堆上("},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":")上分配对应的数量的内存页(每页大小是8KB)给程序。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5a/5a1bddc841eb177b0a6c280b76279f79.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们把内存分配管理涉及的所有概念串起来,可以勾画出Go内存管理的一个全局视图:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5f/5f19bbede074d83d2d28295cfb9500ab.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Go语言的内存分配非常复杂,这个文章从一个比较粗的角度来看Go的内存分配,并没有深入细节。一般而言,了解它的原理,到这个程度也就可以了(应付面试)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"总结起来关于Go内存分配管理的策略有如下几点:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Go在程序启动时,会向操作系统申请一大块内存,由"},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"结构全局管理。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Go内存管理的基本单元是"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":",每种"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"可以分配特定大小的"},{"type":"codeinline","content":[{"type":"text","text":"object"}]},{"type":"text","text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"是"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"内存管理的三大组件,"},{"type":"codeinline","content":[{"type":"text","text":"mcache"}]},{"type":"text","text":"管理线程在本地缓存的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":";"},{"type":"codeinline","content":[{"type":"text","text":"mcentral"}]},{"type":"text","text":"管理全局的"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"供所有线程使用;"},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"管理"},{"type":"codeinline","content":[{"type":"text","text":"Go"}]},{"type":"text","text":"的所有动态分配内存。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般小对象通过"},{"type":"codeinline","content":[{"type":"text","text":"mspan"}]},{"type":"text","text":"分配内存;大对象则直接由"},{"type":"codeinline","content":[{"type":"text","text":"mheap"}]},{"type":"text","text":"分配内存。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"相关阅读"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzUzNTY5MzU2MA==&mid=2247485579&idx=1&sn=f481cff4ffccacc186a020e45e884924&chksm=fa80d91ccdf7500ab984ecde7056aa29a2986c423557736c32bd251f2cb5d1ce1e54909753cc&token=1603590138&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"Go内存管理之代码的逃逸分析"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s?_biz=MzUzNTY5MzU2MA==&mid=2247484559&idx=1&sn=d639ce831d514c0f73f195f207c0583d&chksm=fa80d518cdf75c0e224697bd9f6f97ad94082f09591d2b9141974baf8a1054e9de0d895fb603&token=1603590138&lang=zhCN#rd","title":""},"content":[{"type":"text","text":"上周并发题的解题思路以及介绍Go语言调度器"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"参考链接"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://medium.com/a-journey-with-go/go-memory-management-and-allocation-a7396d430f44","title":""},"content":[{"type":"text","text":" Memory Management and Allocation"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.im/post/6844903795739082760#heading-7","title":""},"content":[{"type":"text","text":"图解Go语言内存分配"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-memory-allocator/","title":""},"content":[{"type":"text","text":"内存分配器"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"感谢你阅读我的文章,微信搜索公众号「网管叨bi叨」第一时间获取更多原创图解Go语言原理的文章。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章