<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-8-interior-pointers-meuxddnix" -->

---
title: Writing a .NET Garbage Collector in C# - Part 8:...
description: Part 8 of a series building a .NET garbage collector from scratch in C# tackles interior pointers - references pointing inside an object rather than to its...
canonical: https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-8-interior-pointers-meuxddnix
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Writing a .NET Garbage Collector in C# - Part 8: Interior pointers | daily.dev
og:description: Part 8 of a series building a .NET garbage collector from scratch in C# tackles interior pointers - references pointing inside an object rather than to its...
og:url: https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-8-interior-pointers-meuxddnix
og:image: https://api.daily.dev/og/posts/mEuxddnIx.png
og:image:alt: Writing a .NET Garbage Collector in C# - Part 8: Interior pointers
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Writing a .NET Garbage Collector in C# - Part 8: Interior pointers

**[Kevin Gosse \(minidump.net\)](https://daily.dev/sources/minidump)** · 15 min read · 0 upvotes · 0 comments

## Summary

Part 8 of a series building a .NET garbage collector from scratch in C# tackles interior pointers - references pointing inside an object rather than to its start, common with spans and CollectionsMarshal. The piece explains why identifying an object's start from an interior pointer is hard, why naive heuristics fail, and why scanning the whole heap segment is too slow. It introduces the brick table technique used by the real .NET GC: a compact offset array (roughly 500 KB per GB of heap) mapping memory regions to nearby object starts, enabling fast backward lookups. The author implements a simplified brick table per segment, updates ScanRoots to resolve interior pointers via brick-table lookup plus a short heap walk, and marks objects into the brick table during the mark phase. The piece closes by flagging that segment lookup is currently inefficient and will be redesigned in the next installment.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://minidump.net/writing-a-net-gc-in-c-part-8>

## Questions this post answers

### What is a brick table and why does a garbage collector need one for interior pointers?

A brick table is a compact array mapping regions of heap memory to the address of a nearby object, letting a garbage collector find an object's start from an interior pointer without scanning the whole segment. Implemented as a byte array where each byte covers roughly 2040 bytes (255*8, accounting for 8-byte alignment), it needs about 500 KB to cover a 1 GB heap, far less than an exhaustive bitmap's 16 MB.

_Developers building or debugging GC-level memory tracking can dig deeper into brick table design on daily.dev._

### Why can't a garbage collector just look for a valid method-table pointer to find the start of an object from an interior pointer?

Scanning backward for something resembling a method-table pointer produces false positives, because arbitrary data such as a stored type handle value or even a coincidental hashcode can match a valid method-table address. Since this pattern-matching approach can never be made fully safe, the only guaranteed fallback is walking the heap from the start of the memory segment, which is too slow to do on every interior pointer lookup.

_Anyone reasoning about GC internals and pointer safety can explore this trade-off further via daily.dev._

### How does CollectionsMarshal.GetValueRefOrAddDefault avoid a double dictionary lookup in C#?

It returns a direct reference to the value slot inside the dictionary's underlying array, so a caller can increment or mutate the value in place instead of performing a separate get and set. This creates an interior pointer into the dictionary's internal array, which the garbage collector must track just like a span to avoid reading invalid memory after the array reference itself is no longer live.

_Developers optimizing hot paths with interior references can track patterns like this on daily.dev._

## Similar posts on daily.dev

- [The Fasting Developer: Memory Discipline and GC Awareness in .NET](https://daily.dev/posts/the-fasting-developer-memory-discipline-and-gc-awareness-in-net-5yq85h552) · We Are .NET · 12 upvotes · 0 comments
- [Internals for Interns](https://daily.dev/posts/internals-for-interns-fg0xibngw) · Hacker News · 0 upvotes · 0 comments
- [Baby’s Second Garbage Collector](https://daily.dev/posts/baby-s-second-garbage-collector-cjrbmht48) · Lobsters · 0 upvotes · 0 comments

---

Tags: [#.net](https://daily.dev/tags/.net), [#c#](https://daily.dev/tags/c#)

[View this post on daily.dev](https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-8-interior-pointers-meuxddnix)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Writing a .NET Garbage Collector in C# - Part 8: Interior pointers","url":"https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-8-interior-pointers-meuxddnix","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-8-interior-pointers-meuxddnix"},"datePublished":"2026-10-04T13:06:38.928Z","dateModified":"2026-10-04T13:15:44.552Z","description":"Part 8 of a series building a .NET garbage collector from scratch in C# tackles interior pointers - references pointing inside an object rather than to its...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/33c86e80a0a4702c8ffad143d626e7bb?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/33c86e80a0a4702c8ffad143d626e7bb?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Kevin Gosse (minidump.net)","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Kevin Gosse (minidump.net)","logo":"https://media.daily.dev/image/upload/s--8KHn1aFB--/f_auto,q_auto/v1791119046/logos/minidump?_a=BAMAMicg0","url":"https://daily.dev/sources/minidump"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-8-interior-pointers-meuxddnix","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":".net,c#","timeRequired":"PT15M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Kevin Gosse (minidump.net)","item":"https://daily.dev/sources/minidump"},{"@type":"ListItem","position":3,"name":"Writing a .NET Garbage Collector in C# - Part 8: Interior pointers"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-8-interior-pointers-meuxddnix#faq","mainEntity":[{"@type":"Question","name":"What is a brick table and why does a garbage collector need one for interior pointers?","acceptedAnswer":{"@type":"Answer","text":"A brick table is a compact array mapping regions of heap memory to the address of a nearby object, letting a garbage collector find an object's start from an interior pointer without scanning the whole segment. Implemented as a byte array where each byte covers roughly 2040 bytes (255*8, accounting for 8-byte alignment), it needs about 500 KB to cover a 1 GB heap, far less than an exhaustive bitmap's 16 MB. Developers building or debugging GC-level memory tracking can dig deeper into brick table design on daily.dev."}},{"@type":"Question","name":"Why can't a garbage collector just look for a valid method-table pointer to find the start of an object from an interior pointer?","acceptedAnswer":{"@type":"Answer","text":"Scanning backward for something resembling a method-table pointer produces false positives, because arbitrary data such as a stored type handle value or even a coincidental hashcode can match a valid method-table address. Since this pattern-matching approach can never be made fully safe, the only guaranteed fallback is walking the heap from the start of the memory segment, which is too slow to do on every interior pointer lookup. Anyone reasoning about GC internals and pointer safety can explore this trade-off further via daily.dev."}},{"@type":"Question","name":"How does CollectionsMarshal.GetValueRefOrAddDefault avoid a double dictionary lookup in C#?","acceptedAnswer":{"@type":"Answer","text":"It returns a direct reference to the value slot inside the dictionary's underlying array, so a caller can increment or mutate the value in place instead of performing a separate get and set. This creates an interior pointer into the dictionary's internal array, which the garbage collector must track just like a span to avoid reading invalid memory after the array reference itself is no longer live. Developers optimizing hot paths with interior references can track patterns like this on daily.dev."}}]}
```

