Last updated: December 13, 2025
Author: Paul Namalomba
- SESKA Computational Engineer
- Software Developer
- PhD Candidate (Civil Engineering Spec. Computational and Applied Mechanics)
Contact: kabwenzenamalomba@gmail.com
Website: paulnamalomba.github.io
This guide explains how C# and the .NET runtime (CoreCLR/CLR) implement memory management and execution semantics on Windows. Covering value vs reference types, stack vs heap storage, copy semantics, and nullability features (nullable reference types, Nullable<T>), plus examples and best practices.
powershell
# Create console project
dotnet new console -n MemoryDemo
cd MemoryDemo
# Replace Program.cs with main.cs if needed, then run
dotnet run
Program.cs with Main method and run dotnet run in the project directory.This guide explains how C# and the .NET runtime (CoreCLR/CLR) implement memory management and execution semantics on Windows. Covering value vs reference types, stack vs heap storage, copy semantics, and nullability features (nullable reference types, Nullable<T>), plus examples and best practices.
struct) vs reference types (class)ICloneable, copy constructorsnull, nullable reference types, Nullable<T>int, double), struct, enum. Stored inline where declared (stack or inside objects). Passed by value by default.class, interface, delegate, object, arrays — variables hold a reference to the object on the managed heap.Table comparison:
| Aspect | Value Types | Reference Types |
|---|---|---|
| Examples | int, struct Point |
class Person, string |
| Storage | Inline (stack or embedded) | Managed heap (GC) |
| Passing | Copy of value | Copy of reference |
| Nullability | Nullable<T> (int?) |
Can be null, but nullable reference types provide annotations |
Example:
struct Point { public int X; public int Y; }
class Person { public string Name { get; set; } }
void Demo() {
Point p1 = new Point { X = 1, Y = 2 };
Point p2 = p1; // copy
Person a = new Person { Name = "Paul" };
Person b = a; // reference copy
b.Name = "Changed"; // a.Name == "Changed"
}
Notes:
- string is a reference type but immutable: assignment yields reference copy; modifications produce new strings.
~Type()) run on separate finalizer thread.Windows/.NET specifics:
- The CLR uses the OS virtual memory and the GC has options (Workstation vs Server) and settings in runtimeconfig.json.
- Use GC.Collect only in rare cases; prefer letting runtime manage memory.
Example demonstrating GC behavior:
class Demo {
static void CreateObjects() {
for (int i=0;i<100000;i++) {
var o = new object();
}
}
static void Main() {
CreateObjects();
GC.Collect(); // forces collection (not usually recommended)
}
}
Tips:
- Implement IDisposable and using for deterministic unmanaged resource cleanup.
- Use WeakReference<T> for cache-like scenarios where you don't want to keep objects alive.
ICloneable) or serialization-based copy.Example (shallow vs deep):
class Book { public string Title; public List<string> Authors = new(); }
void Demo() {
var b1 = new Book { Title = "A", Authors = new List<string>{"X"} };
var b2 = b1; // shallow (reference copy)
b2.Authors.Add("Y"); // b1.Authors also changed
// Deep copy (manual)
var b3 = new Book { Title = b1.Title, Authors = new List<string>(b1.Authors) };
}
Best practices:
- Prefer immutable types or make deep copies when necessary.
- Use MemberwiseClone carefully; implement custom cloning when needed.
null for reference types; C#8 introduced nullable reference types (string?) and static analysis to reduce null-related bugs.Nullable<T> (int?) wraps value types to allow null.Example:
#nullable enable
string? maybe = null;
if (maybe != null) Console.WriteLine(maybe.Length);
int? maybeInt = null;
if (maybeInt.HasValue) Console.WriteLine(maybeInt.Value);
Tips:
- Enable nullable reference types in csproj (<Nullable>enable</Nullable>).
- Use null-coalescing ?? and ?. for safe access.
Program.cs:
using System;
using System.Collections.Generic;
class Program {
static void Main() {
// Value vs Reference
var p1 = new Point { X = 1, Y = 2 };
var p2 = p1; // copied
var book1 = new Book { Title = "C#" };
var book2 = book1; // reference copy
book2.Title = "Changed";
Console.WriteLine(book1.Title); // "Changed"
// Nullable
string? s = null;
Console.WriteLine(s ?? "(null)");
}
}
struct Point { public int X; public int Y; }
class Book { public string Title; public List<string> Authors = new(); }
Run with dotnet run in project folder.
DOTNET_gcServer, DOTNET_gcConcurrent environment variables.dotnet-dump, procdump, WinDbg for post-mortem analysis.dotnet-gcdump and dotnet-trace for diagnostics.End of C# guide.