<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Chapter 12 Memory Allocator on Go: Under the Hood</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/</link><description>Recent content in Chapter 12 Memory Allocator on Go: Under the Hood</description><generator>Hugo</generator><language>en</language><atom:link href="https://golang.design/under-the-hood/en/part4memory/ch12alloc/index.xml" rel="self" type="application/rss+xml"/><item><title>12.1 Design Principles</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/basic/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/en/part4memory/ch12alloc/basic/</guid><description>&lt;h1 id="121-design-principles"&gt;12.1 Design Principles&lt;/h1&gt;
&lt;p&gt;By now we have seen how a Go program starts up, and how the scheduler spreads goroutines across operating-system threads for execution. The single thing that scheduled code does most often is request memory: every &lt;code&gt;new&lt;/code&gt;, every local variable that escapes to the heap, every slice growth, all funnel into the same entry point, &lt;code&gt;runtime.mallocgc&lt;/code&gt;. This chapter is about the allocator behind that entry point. This section does not yet touch its parts; it answers a question that comes first: what mutually pulling goals must a memory allocator built for Go satisfy, and on which few judgments does it settle them? Once you understand these trade-offs, the structures and paths in the following sections (&lt;a href=".././component"&gt;12.2&lt;/a&gt;–&lt;a href=".././tinyalloc"&gt;12.6&lt;/a&gt;) will all have a clear origin.&lt;/p&gt;</description></item><item><title>12.2 Components</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/component/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/en/part4memory/ch12alloc/component/</guid><description>&lt;h1 id="122-components"&gt;12.2 Components&lt;/h1&gt;
&lt;p&gt;&lt;a href=".././basic"&gt;12.1&lt;/a&gt; described the allocator as a layered structure that is &amp;ldquo;lock-free on the fast path, locked on the slow path.&amp;rdquo; This section names and locates the few core components of that structure, and grounds them in their actual form in go1.26: what state each component carries, why it is designed the way it is, and how they chain together into a restocking pipeline. Once you have understood these few things, the later allocation paths (&lt;a href=".././largealloc"&gt;12.4&lt;/a&gt;–&lt;a href=".././tinyalloc"&gt;12.6&lt;/a&gt;) are just &amp;ldquo;a walk across this same picture.&amp;rdquo;&lt;/p&gt;</description></item><item><title>12.3 Initialization</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/init/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/en/part4memory/ch12alloc/init/</guid><description>&lt;h1 id="123-initialization"&gt;12.3 Initialization&lt;/h1&gt;
&lt;p&gt;&lt;a href=".././component"&gt;12.2&lt;/a&gt; broke the allocator down into a few parts: mcache, mcentral, mheap, and arena. But that was a
static picture. These parts do not fall into place on their own; they must be set up once, at the very start of the
program. This section answers the question: when &lt;code&gt;main&lt;/code&gt; has not yet run and the first &lt;code&gt;new&lt;/code&gt; has not yet happened, what
infrastructure does the runtime lay down for the allocator.&lt;/p&gt;</description></item><item><title>12.4 Large Object Allocation</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/largealloc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/en/part4memory/ch12alloc/largealloc/</guid><description>&lt;h1 id="124-large-object-allocation"&gt;12.4 Large Object Allocation&lt;/h1&gt;
&lt;p&gt;The allocation hierarchy of &lt;a href=".././component"&gt;12.2&lt;/a&gt; was built for objects that are &amp;ldquo;small and frequent&amp;rdquo;: a per-P mcache, a mcentral shared by size class, and a global mheap. These three cache layers collapse the vast majority of allocations into a handful of lock-free bit operations. But this delicate machine carries an implicit premise, that the object is small enough to fit into a size class. Once an object exceeds &lt;code&gt;maxSmallSize&lt;/code&gt; (32768 bytes in go1.26, that is 32KB), it no longer fits into any size class slot, and the whole replenishment chain loses its meaning for it.&lt;/p&gt;</description></item><item><title>12.5 Small Object Allocation</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/smallalloc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/en/part4memory/ch12alloc/smallalloc/</guid><description>&lt;h1 id="125-small-object-allocation"&gt;12.5 Small Object Allocation&lt;/h1&gt;
&lt;p&gt;Small objects are those whose size falls between 16B and 32KB (in go1.26, &lt;code&gt;maxSmallSize = 32768&lt;/code&gt;). They are the most common kind of allocation in a Go program: a struct, the backing array of a modest slice, the result of a string concatenation, the vast majority land in this range. The path the allocator designs for them is the main stage of the mcache → mcentral → mheap hierarchy from &lt;a href=".././component"&gt;12.2&lt;/a&gt;, and it is where the whole allocator&amp;rsquo;s &amp;ldquo;lock-free fast path&amp;rdquo; design (&lt;a href=".././basic"&gt;12.1&lt;/a&gt;) makes good on its performance promise.&lt;/p&gt;</description></item><item><title>12.6 Tiny Object Allocation</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/tinyalloc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/en/part4memory/ch12alloc/tinyalloc/</guid><description>&lt;h1 id="126-tiny-object-allocation"&gt;12.6 Tiny Object Allocation&lt;/h1&gt;
&lt;p&gt;The previous two sections walked through the two ends of the allocator. Large objects (&lt;a href=".././largealloc"&gt;12.4&lt;/a&gt;) bypass the cache and ask the mheap for memory by the page directly; small objects (&lt;a href=".././smallalloc"&gt;12.5&lt;/a&gt;) pull an equal-sized slot from the per-P mcache according to their size class. This section fills in the last and least conspicuous category of object, the &lt;strong&gt;tiny object&lt;/strong&gt;: those smaller than 16 bytes and free of pointers. They are extremely numerous and individually tiny, so if each were also given a slot by size class, the waste would be astonishing. Go sets up a dedicated path for them, packing multiple tiny objects &lt;strong&gt;into the same block&lt;/strong&gt;, and trades one simple &amp;ldquo;bump pointer&amp;rdquo; technique for sizable memory savings.&lt;/p&gt;</description></item><item><title>12.7 The Page Allocator</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/pagealloc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/en/part4memory/ch12alloc/pagealloc/</guid><description>&lt;h1 id="127-the-page-allocator"&gt;12.7 The Page Allocator&lt;/h1&gt;
&lt;p&gt;Beneath mheap (&lt;a href=".././component"&gt;12.2&lt;/a&gt;), the component that answers &amp;ldquo;which pages are free, which are in use&amp;rdquo; is the page allocator. It is the foundation of the entire allocator: when an mcentral runs out of spans it asks mheap for new pages, large objects (&lt;a href=".././largealloc"&gt;12.4&lt;/a&gt;) are requested directly by page, and every page a span occupies is ultimately carved out from here. The page allocator must also return pages that have stayed idle for a long time back to the operating system. These two responsibilities, one tied to the speed of allocation and the other to the resident memory of the process, are exactly the most enduring tension in Go&amp;rsquo;s memory system.&lt;/p&gt;</description></item><item><title>12.8 Memory Statistics</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/mstats/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/en/part4memory/ch12alloc/mstats/</guid><description>&lt;h1 id="128-memory-statistics"&gt;12.8 Memory Statistics&lt;/h1&gt;
&lt;p&gt;The allocator keeps books while it works. Every time it wholesales a stretch of address space from the operating system, carves out a span, or allocates or sweeps an object, the runtime accumulates the corresponding count into a set of global variables (&lt;code&gt;runtime.memstats&lt;/code&gt;). These books are not a statistical report compiled after the fact; they are a running ledger written down in passing during allocation and reclamation. They serve two ends. Outwardly, they let users and monitoring systems see the memory shape of the process. Inwardly, both &lt;a href="../../ch13gc/pacing"&gt;the GC pacer (13.3)&lt;/a&gt; and &lt;a href=".././pagealloc"&gt;the soft memory limit &lt;code&gt;GOMEMLIMIT&lt;/code&gt; (12.7)&lt;/a&gt; must read these books to decide &amp;ldquo;at what heap size should the next round of reclamation be triggered.&amp;rdquo; In other words, the bookkeeping closes a feedback loop: allocation produces data, data drives decisions, and decisions in turn constrain allocation.&lt;/p&gt;</description></item><item><title>12.9 Past, Present, and Future</title><link>https://golang.design/under-the-hood/en/part4memory/ch12alloc/history/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://golang.design/under-the-hood/en/part4memory/ch12alloc/history/</guid><description>&lt;h1 id="129-past-present-and-future"&gt;12.9 Past, Present, and Future&lt;/h1&gt;
&lt;p&gt;The allocator did not arrive fully formed. It has been polished over and over as Go evolved. Tracing this line of evolution makes clear which trade-offs the current design grew out of, and gives a glimpse of where it is headed. One judgment worth keeping in mind first: nearly every major change to the allocator was not made to make &amp;ldquo;allocation itself faster,&amp;rdquo; but to re-place a stone in the three-way game among allocation speed, memory footprint, and cooperation with the garbage collector. Once this main thread is read, the several rewrites below stop looking like isolated version trivia and become instead the same engineering problem being solved again and again.&lt;/p&gt;</description></item></channel></rss>