<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-9-frozen-segments-and-new-allocation-strategy-wubvy9lsu" -->

---
title: Writing a .NET Garbage Collector in C# - Part 9: Frozen...
description: This installment of a series on writing a custom .NET garbage collector in C# explains frozen segments and the NonGC heap, which store immortal objects like...
canonical: https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-9-frozen-segments-and-new-allocation-strategy-wubvy9lsu
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 9: Frozen segments and new allocation strategy | daily.dev
og:description: This installment of a series on writing a custom .NET garbage collector in C# explains frozen segments and the NonGC heap, which store immortal objects like...
og:url: https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-9-frozen-segments-and-new-allocation-strategy-wubvy9lsu
og:image: https://api.daily.dev/og/posts/wuBvY9Lsu.png
og:image:alt: Writing a .NET Garbage Collector in C# - Part 9: Frozen segments and new allocation strategy
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 9: Frozen segments and new allocation strategy

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

## Summary

This installment of a series on writing a custom .NET garbage collector in C# explains frozen segments and the NonGC heap, which store immortal objects like string literals so the GC can skip scanning them. It walks through a real bug where marking objects in frozen segments corrupts their method-table pointer during sweep, then explores fixes: walking frozen segments to unmark (costly), checking IsInFrozenSegment per object (too slow), and finally a better approach - reserving a huge contiguous virtual address range (2 TB via VirtualAlloc/mmap) so any address outside that range is known not to be GC-managed. A new NativeAllocator replacing NativeMemory.Alloc is implemented, committing memory on demand and exposing a cheap O(1) IsInRange check used during mark phase, avoiding the need to ever mark or unmark frozen segment objects.

## 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-9>

## Questions this post answers

### Why does marking a string literal during garbage collection crash the program when it's later checked with 'is string'?

The crash happens because the string literal lives in a frozen segment outside the normal managed heap, so when the mark phase sets a mark bit on its method-table pointer, the sweep phase never walks that segment to unmark it afterward. The object is left with a corrupted method-table pointer, so any later type check on it reads garbage and crashes.

_Developers debugging GC-related crashes with frozen segments or string literals can find implementation walkthroughs like this on daily.dev._

### How can a garbage collector cheaply check if an object pointer belongs to the managed heap versus unmanaged memory?

Reserve a single huge contiguous virtual address range upfront (for example 2 TB via VirtualAlloc on Windows or mmap on Linux) and commit pages from it on demand as objects are allocated. Then checking whether a pointer is managed becomes a simple comparison against the reserved range's lower and upper bounds, an O(1) test, instead of searching through separate frozen segments.

_Engineers designing memory allocators or GC internals can track techniques like this via 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
- [Win16 Memory Management](https://daily.dev/posts/win16-memory-management-iurgwfefx) · Lobsters · 0 upvotes · 0 comments
- [Internals for Interns](https://daily.dev/posts/internals-for-interns-fg0xibngw) · Hacker News · 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-9-frozen-segments-and-new-allocation-strategy-wubvy9lsu)

```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 9: Frozen segments and new allocation strategy","url":"https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-9-frozen-segments-and-new-allocation-strategy-wubvy9lsu","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-9-frozen-segments-and-new-allocation-strategy-wubvy9lsu"},"datePublished":"2026-10-04T13:06:42.821Z","dateModified":"2026-10-04T13:15:38.623Z","description":"This installment of a series on writing a custom .NET garbage collector in C# explains frozen segments and the NonGC heap, which store immortal objects like...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/a93e8b78d12e43a93d826c59371147d0?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/a93e8b78d12e43a93d826c59371147d0?_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-9-frozen-segments-and-new-allocation-strategy-wubvy9lsu","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":".net,c#","timeRequired":"PT14M"}
{"@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 9: Frozen segments and new allocation strategy"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/writing-a-net-garbage-collector-in-c---part-9-frozen-segments-and-new-allocation-strategy-wubvy9lsu#faq","mainEntity":[{"@type":"Question","name":"Why does marking a string literal during garbage collection crash the program when it's later checked with 'is string'?","acceptedAnswer":{"@type":"Answer","text":"The crash happens because the string literal lives in a frozen segment outside the normal managed heap, so when the mark phase sets a mark bit on its method-table pointer, the sweep phase never walks that segment to unmark it afterward. The object is left with a corrupted method-table pointer, so any later type check on it reads garbage and crashes. Developers debugging GC-related crashes with frozen segments or string literals can find implementation walkthroughs like this on daily.dev."}},{"@type":"Question","name":"How can a garbage collector cheaply check if an object pointer belongs to the managed heap versus unmanaged memory?","acceptedAnswer":{"@type":"Answer","text":"Reserve a single huge contiguous virtual address range upfront (for example 2 TB via VirtualAlloc on Windows or mmap on Linux) and commit pages from it on demand as objects are allocated. Then checking whether a pointer is managed becomes a simple comparison against the reserved range's lower and upper bounds, an O(1) test, instead of searching through separate frozen segments. Engineers designing memory allocators or GC internals can track techniques like this via daily.dev."}}]}
```

