<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Clang on Tachera W Sasi</title><link>https://tacherasasi.github.io/tags/clang/</link><description>Recent content in Clang on Tachera W Sasi</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 13 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tacherasasi.github.io/tags/clang/index.xml" rel="self" type="application/rss+xml"/><item><title>What Building malloc Taught Me About Memory</title><link>https://tacherasasi.github.io/posts/what-building-malloc-taught-me-about-memory-57eae5b35166/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/what-building-malloc-taught-me-about-memory-57eae5b35166/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*2dOqSEchtIkDv2Fma09rOA.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Modern developers rarely think about memory.&lt;/p&gt;
&lt;p&gt;We call malloc, new, or let a garbage collector handle everything. Most of the time that’s perfectly fine. But recently I decided to dig deeper and implement a very small version of malloc myself.&lt;/p&gt;
&lt;p&gt;Not to replace real allocators. Just to understand what actually happens under the hood.&lt;/p&gt;
&lt;p&gt;The result was surprisingly simple and incredibly educational.&lt;/p&gt;
&lt;p&gt;This post walks through the core ideas.&lt;/p&gt;
&lt;h3 id="why-understanding-memory-still-matters"&gt;Why Understanding Memory Still Matters&lt;/h3&gt;
&lt;p&gt;Most software today runs on layers of abstraction.&lt;/p&gt;
&lt;p&gt;Frameworks → languages → runtimes → operating systems.&lt;/p&gt;
&lt;p&gt;That’s powerful, but it also means many developers never see the &lt;strong&gt;foundations&lt;/strong&gt; those layers are built on.&lt;/p&gt;
&lt;p&gt;Understanding memory allocation helps with:&lt;/p&gt;
&lt;p&gt;• writing faster software&lt;br&gt;
• debugging memory issues&lt;br&gt;
• understanding how runtimes work&lt;br&gt;
• building systems tools&lt;br&gt;
• designing better APIs&lt;/p&gt;
&lt;p&gt;Even if you work in higher level languages like &lt;strong&gt;Go&lt;/strong&gt; , these concepts still power everything underneath.&lt;/p&gt;
&lt;h3 id="step-1-where-malloc-gets-memory"&gt;Step 1: Where malloc Gets Memory&lt;/h3&gt;
&lt;p&gt;When a program runs, memory roughly looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Globals
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Heap ← grows upward
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Stack ← grows downward
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;malloc allocates from the &lt;strong&gt;heap&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Under the hood, the allocator asks the OS for more heap space using a system call like sbrk().&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;void *ptr = sbrk(1024);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This moves the &lt;strong&gt;program break&lt;/strong&gt; forward by 1024 bytes and returns a pointer to the new memory.&lt;/p&gt;
&lt;p&gt;Think of it like extending the end of your program’s heap.&lt;/p&gt;
&lt;h3 id="step-2-the-naive-allocator"&gt;Step 2: The Naive Allocator&lt;/h3&gt;
&lt;p&gt;The simplest possible allocator would just call sbrk.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;void *my_malloc(size_t size) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return sbrk(size);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this immediately creates a problem.&lt;/p&gt;
&lt;p&gt;If you later call free, the allocator has &lt;strong&gt;no idea&lt;/strong&gt; :&lt;/p&gt;
&lt;p&gt;• how big the allocation was&lt;br&gt;
• where the next block starts&lt;br&gt;
• whether a block can be reused&lt;/p&gt;
&lt;p&gt;So real allocators store &lt;strong&gt;metadata&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="step-3-the-hidden-header-trick"&gt;Step 3: The Hidden Header Trick&lt;/h3&gt;
&lt;p&gt;Every allocation secretly stores a small header before the memory returned to the user.&lt;/p&gt;
&lt;p&gt;Memory layout:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ HEADER ][ USER MEMORY ]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The header might contain:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;size
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;is_free
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;next_block
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Example structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&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="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;block_header&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="n"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size&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="n"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is_free&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="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;block_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;block_header_t&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When the allocator returns memory, it actually returns a pointer &lt;strong&gt;after the header&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;return (void *)(block + 1);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which means the user sees:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ptr → usable memory
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While the allocator still has its metadata.&lt;/p&gt;
&lt;h3 id="step-4-tracking-allocations"&gt;Step 4: Tracking Allocations&lt;/h3&gt;
&lt;p&gt;Blocks are usually tracked using a linked list.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;heap_start
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[block A] → [block B] → [block C]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When malloc runs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;search for a free block big enough&lt;/li&gt;
&lt;li&gt;reuse it if possible&lt;/li&gt;
&lt;li&gt;otherwise request more memory from the OS&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This keeps allocations fast and avoids unnecessary system calls.&lt;/p&gt;
&lt;h3 id="step-5-what-free-actually-does"&gt;Step 5: What free Actually Does&lt;/h3&gt;
&lt;p&gt;One surprising thing: free usually does &lt;strong&gt;not return memory to the OS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead it just marks the block as reusable.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[HEADER free=1][memory]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Future allocations can reuse that block instead of requesting more memory.&lt;/p&gt;
&lt;h3 id="what-real-allocators-do-differently"&gt;What Real Allocators Do Differently&lt;/h3&gt;
&lt;p&gt;Real allocators (like the ones used by &lt;strong&gt;glibc&lt;/strong&gt;) add several optimizations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Block splitting&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Free block: 100 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Need: 20
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Split into:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;20 used
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;80 still free
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Block coalescing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Adjacent free blocks merge together to reduce fragmentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Large allocation handling&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Large allocations often use mmap instead of the heap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thread safety&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Modern allocators maintain thread local arenas for performance.&lt;/p&gt;
&lt;h3 id="what-i-learned-from-this"&gt;What I Learned From This&lt;/h3&gt;
&lt;p&gt;Building even a tiny allocator reinforced a few important lessons.&lt;/p&gt;
&lt;h3 id="abstractions-hide-complexity-but-the-fundamentals-still-matter"&gt;Abstractions hide complexity, but the fundamentals still matter&lt;/h3&gt;
&lt;p&gt;Languages like &lt;strong&gt;Go&lt;/strong&gt; make memory management easier, but the underlying principles are still there.&lt;/p&gt;
&lt;h3 id="performance-often-comes-from-understanding-the-layers-below-you"&gt;Performance often comes from understanding the layers below you&lt;/h3&gt;
&lt;p&gt;Understanding memory layout and allocation patterns helps explain why some code performs better than others.&lt;/p&gt;
&lt;h3 id="systems-knowledge-compounds"&gt;Systems knowledge compounds&lt;/h3&gt;
&lt;p&gt;Once you understand memory allocators, many other topics suddenly make more sense:&lt;/p&gt;
&lt;p&gt;• garbage collectors&lt;br&gt;
• runtime design&lt;br&gt;
• kernel memory management&lt;br&gt;
• high performance servers&lt;/p&gt;
&lt;h3 id="final-thoughts"&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;You don’t need to write your own allocator for production software.&lt;/p&gt;
&lt;p&gt;But building one, even a small version, forces you to think like the runtime.&lt;/p&gt;
&lt;p&gt;And that perspective is valuable.&lt;/p&gt;
&lt;p&gt;The more you understand the layers beneath your tools, the better engineer you become.&lt;/p&gt;
&lt;p&gt;GITHUB =&amp;gt; &lt;a href="https://github.com/tacheraSasi/malloc.git"&gt;https://github.com/tacheraSasi/malloc.git&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=57eae5b35166" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/what-building-malloc-taught-me-about-memory-57eae5b35166?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>