<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/exploring-net-frozen-segments-eeibqmwr8" -->

---
title: Exploring .NET frozen segments | daily.dev
description: A deep technical exploration of .NET's hidden frozen segments API, which allows developers to allocate managed reference objects (strings, arrays, objects,...
canonical: https://daily.dev/posts/exploring-net-frozen-segments-eeibqmwr8
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Exploring .NET frozen segments | daily.dev
og:description: A deep technical exploration of .NET's hidden frozen segments API, which allows developers to allocate managed reference objects (strings, arrays, objects,...
og:url: https://daily.dev/posts/exploring-net-frozen-segments-eeibqmwr8
og:image: https://api.daily.dev/og/posts/EeiBQmwR8.png
og:image:alt: Exploring .NET frozen segments
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.

# Exploring .NET frozen segments

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

## Summary

A deep technical exploration of .NET's hidden frozen segments API, which allows developers to allocate managed reference objects (strings, arrays, objects, structs) directly in native memory outside the garbage-collected heap, bypassing the normal `new` allocator entirely. The piece walks through calling the private GC._RegisterFrozenSegment method via reflection, manually constructing object headers and method-table pointers byte-by-byte, handling constructors via reflection or function pointers, and building a working bump-pointer allocator class. It also covers subtle pitfalls: references from frozen segments to the managed heap behave as weak references, and sizeof() is unreliable for computing object layout sizes (the method table's BaseSize field must be used instead). An addendum, added after community feedback from a .NET runtime engineer, explains that in some GC configurations frozen segments can actually be scanned, requiring either a dummy filler object or direct manipulation of the internal Heap_segment structure's allocated field to avoid corruption.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://minidump.net/exploring-frozen-segments>

## Questions this post answers

### Why does GC.GetGeneration return 2147483647 for a string literal in .NET 8?

That value indicates the object lives in the NonGC heap, introduced in .NET 8, which is built on frozen segments and is ignored entirely by the garbage collector. The runtime uses it automatically for objects guaranteed to live forever, such as string literals, enabling JIT optimizations because the object is guaranteed never to move.

_daily.dev surfaces deep-dive explainers like this for developers debugging unexpected GC generation values._

### How do I allocate a managed object in native memory in .NET outside the normal GC heap?

Use the private, undocumented GC._RegisterFrozenSegment method (accessed via reflection) to register a block of native memory allocated with NativeMemory.AlignedAlloc as a frozen segment, then manually write the object header (0), the method-table pointer from typeof(T).TypeHandle.Value, and the object's data fields byte by byte, since the normal `new` operator cannot target specific memory locations.

_developers exploring low-level .NET memory tricks can find this kind of unsafe-API walkthrough on daily.dev._

### Why does sizeof() give the wrong object size in C# for computing heap layout?

sizeof() applied to a reference type returns the size of the reference pointer (IntPtr.Size), not the actual size of the object on the heap. To get the true allocated size, read the BaseSize field from the type's method table (via typeof(T).TypeHandle.Value cast to a MethodTable struct), and for arrays also use ComponentSize to account for element size.

_engineers building custom allocators can rely on daily.dev to track these low-level .NET sizing gotchas._

## 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
- [strings](https://daily.dev/posts/strings-2yiygpffb) · Inside Java · 0 upvotes · 0 comments
- [Internals for Interns](https://daily.dev/posts/internals-for-interns-fg0xibngw) · Hacker News · 0 upvotes · 0 comments
- [The hidden trap of fixed buffers in C\#](https://daily.dev/posts/the-hidden-trap-of-fixed-buffers-in-c--87ydebi3d) · Kevin Gosse \(minidump.net\) · 7 upvotes · 1 comments

---

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

[View this post on daily.dev](https://daily.dev/posts/exploring-net-frozen-segments-eeibqmwr8)

```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":"Exploring .NET frozen segments","url":"https://daily.dev/posts/exploring-net-frozen-segments-eeibqmwr8","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/exploring-net-frozen-segments-eeibqmwr8"},"datePublished":"2026-10-04T13:06:38.241Z","dateModified":"2026-10-04T13:15:50.104Z","description":"A deep technical exploration of .NET's hidden frozen segments API, which allows developers to allocate managed reference objects (strings, arrays, objects,...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/4c36525887f37296b6d9f87722e0cd5c?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/4c36525887f37296b6d9f87722e0cd5c?_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/exploring-net-frozen-segments-eeibqmwr8","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":".net","timeRequired":"PT16M"}
{"@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":"Exploring .NET frozen segments"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/exploring-net-frozen-segments-eeibqmwr8#faq","mainEntity":[{"@type":"Question","name":"Why does GC.GetGeneration return 2147483647 for a string literal in .NET 8?","acceptedAnswer":{"@type":"Answer","text":"That value indicates the object lives in the NonGC heap, introduced in .NET 8, which is built on frozen segments and is ignored entirely by the garbage collector. The runtime uses it automatically for objects guaranteed to live forever, such as string literals, enabling JIT optimizations because the object is guaranteed never to move. daily.dev surfaces deep-dive explainers like this for developers debugging unexpected GC generation values."}},{"@type":"Question","name":"How do I allocate a managed object in native memory in .NET outside the normal GC heap?","acceptedAnswer":{"@type":"Answer","text":"Use the private, undocumented GC._RegisterFrozenSegment method (accessed via reflection) to register a block of native memory allocated with NativeMemory.AlignedAlloc as a frozen segment, then manually write the object header (0), the method-table pointer from typeof(T).TypeHandle.Value, and the object's data fields byte by byte, since the normal `new` operator cannot target specific memory locations. developers exploring low-level .NET memory tricks can find this kind of unsafe-API walkthrough on daily.dev."}},{"@type":"Question","name":"Why does sizeof() give the wrong object size in C# for computing heap layout?","acceptedAnswer":{"@type":"Answer","text":"sizeof() applied to a reference type returns the size of the reference pointer (IntPtr.Size), not the actual size of the object on the heap. To get the true allocated size, read the BaseSize field from the type's method table (via typeof(T).TypeHandle.Value cast to a MethodTable struct), and for arrays also use ComponentSize to account for element size. engineers building custom allocators can rely on daily.dev to track these low-level .NET sizing gotchas."}}]}
```

