<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Memory on 我的技术博客</title><link>https://buvidk1234.github.io/tags/memory/</link><description>Recent content in Memory on 我的技术博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Tue, 18 Aug 2026 00:13:00 +0800</lastBuildDate><atom:link href="https://buvidk1234.github.io/tags/memory/index.xml" rel="self" type="application/rss+xml"/><item><title>Go Runtime（四）：内存分配器</title><link>https://buvidk1234.github.io/posts/go-runtime-04-allocator/</link><pubDate>Tue, 18 Aug 2026 00:13:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-04-allocator/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。文中的源码链接固定到 &lt;code&gt;go1.26.2&lt;/code&gt; tag；对象大小阈值、size class 表和局部快路径可能随版本变化。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go 内存分配器要同时满足三类目标：小对象分配足够快，多 P 并发时减少共享锁竞争，GC 又能由任意堆地址快速定位对象边界和标记元数据。它采用按对象规格分级、按 P 缓存、按页统一管理的结构。&lt;/p&gt;
&lt;p&gt;对象是否进入这里首先由编译器逃逸分析决定；本文从已经确定需要堆分配的对象开始，追踪 &lt;code&gt;mallocgc&lt;/code&gt; 到 mcache、mcentral、mheap 和页分配器。&lt;/p&gt;
&lt;h2 id="先确认对象是否真的进入堆"&gt;先确认对象是否真的进入堆&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;new(T)&lt;/code&gt; 不等于堆分配，返回局部变量地址也不必机械地等于堆分配；编译器根据值是否可能在当前栈帧结束后继续可达、地址是否暴露给未知调用等条件做逃逸分析，随后内联和标量替换还可能消除分配。&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;GO&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alloc&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;//go:noinline&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;makeNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;BASH&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; -gcflags&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;all=-m=2&amp;#39;&lt;/span&gt; ./...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; -run&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^$&amp;#39;&lt;/span&gt; -bench&lt;span class="o"&gt;=&lt;/span&gt;. -benchmem ./...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;第一条解释编译器决定，第二条测量最终二进制的每次操作分配。只看源码语法或只看逃逸日志都不足以判断生产路径成本。&lt;/p&gt;
&lt;h2 id="分配器的层级"&gt;分配器的层级&lt;/h2&gt;
&lt;p&gt;Go 将小对象归入 size class，再按 spanClass 管理 span。spanClass 同时编码 size class 和对象是否为 noscan。&lt;/p&gt;</description></item></channel></rss>