Memory & Garbage Collection · Lesson 19

CoreCLR, Mono/WebAssembly, and Unity

Record the execution profile before applying collector diagrams, measurements, or tooling assumptions.

Explore the interactive Memory Lab

Change owners, step through generations, pinning and finalization, or compare memory under load. The model explains concepts; the SDK examples measure real runtime observations.

Open the Memory Lab ↗

The memory contract

C# language semantics travel across several implementations, but collector architecture and diagnostic capabilities do not become identical merely because the source compiles. Desktop CoreCLR, Mono-based WebAssembly execution, and Unity's scripting backends are different contexts. A lesson about CoreCLR generations or a desktop allocation trace must state that target explicitly. SharpRune's bundled .NET9 browser profile executes managed code in its own environment, while Unity has engine and platform-specific memory behavior. Do not infer Unity's collector from a generic diagram labeled C# memory.

Trace the actual program

The sample prints a framework description, the reported maximum generation, and whether the runtime says dynamic code is compiled. All values are observations without a fixed expected output. They help document a run but are not a complete runtime detector: a framework label alone may not distinguish every engine or collector configuration. The program does not claim that a false dynamic-code-compilation flag prevents interpretation of managed code, nor does it claim public generation reporting identifies a physical heap. Record the known host profile beside these values rather than guessing it from one field.

How the mechanism works

CoreCLR's generational documentation describes that runtime's collection strategy, including its associated heap regions and modes. Mono and WebAssembly builds have their own supported implementation choices and constraints. Unity's 6.3 documentation describes the Boehm-Demers-Weiser collector and non-compacting behavior, with incremental collection supported on relevant platforms; its web platform has additional limitations. That is not the CoreCLR zero-one-two model. Incremental collection and generational collection are different concepts, so spreading work across frames does not justify relabeling Unity as a generational CoreCLR collector.

Boundaries and misconceptions

Do not describe a browser run's counters as desktop measurements, or treat a Unity Profiler allocation column as a .NET per-thread counter with identical units and scope. Browser JavaScript objects, WebAssembly memory, managed objects, and engine-native resources can participate in different ownership systems. A .NET9 portable console exercise also is not automatically a Unity editor script: API compatibility, engine lifetimes, build backend, and platform matter. Keep engine-specific examples separate from executable browser code rather than importing unsupported namespaces into a lesson that promises to run here.

Practice: change one ownership path

First run this exact observational source in SharpRune and record the known profile alongside every label. Repeat it in a documented desktop CoreCLR console project if available. Hint: compare observations without turning either set into a universal expected-output assertion. Explain why identical framework descriptions would not prove identical collectors. Add an ordinary deterministic data calculation and show that it can produce the same answer across profiles while the runtime observations differ; portability of semantics and identity of implementation are separate claims.

Practice: test a second boundary

For a second exercise, read the versioned Unity collector documentation and list which statements concern incremental scheduling, compaction, or platform support. Hint: none of those terms by itself asserts CoreCLR-style generations. Design a Unity-specific profiling experiment separately from this portable program, using the engine's supported tools and lifecycle. For a browser memory investigation, state which part of the observed memory belongs to managed execution versus host or interop data, and report unavailable evidence honestly instead of substituting a desktop screenshot.

Review the evidence

A reliable memory note begins with runtime, version, backend, platform, build mode, and the metric's scope. The sample helps record a few capabilities without pretending to fully identify the collector. Use CoreCLR diagrams only with their stated assumptions, treat Unity's versioned documentation as its own source, and verify the actual Mono/WebAssembly profile. This discipline preserves valuable shared ownership lessons while preventing runtime-specific thresholds, generation claims, or profiler results from becoming false promises about another environment.

  • Framework labels and capability values are observations, not a complete collector detector.
  • Unity incremental scheduling is not CoreCLR-style generational collection.
  • Browser, desktop, and engine measurements must retain their own target labels.

A working example

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Buffers;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

Console.WriteLine($"framework:{RuntimeInformation.FrameworkDescription}");
Console.WriteLine($"max-generation:{GC.MaxGeneration}");
Console.WriteLine($"dynamic-code-compiled:{RuntimeFeature.IsDynamicCodeCompiled}");
Runtime-dependent observation

Generation counts and allocation measurements can vary. Follow the lesson’s observation steps rather than matching a fixed transcript.

Try this
Framework labels and capability values are observations, not a complete collector detector.

Further reading