Welcome to a new series where we will dive deep into optimization techniques at the C# language and .NET runtime level. These are the foundational elements that determine the core performance of your application.
C# Core Optimization Series
Part 1: Stack vs. Heap and the Power of struct
Welcome to a new series on C# and .NET optimization. We'll start with the most fundamental concept: where your data is stored. Whether it's on the Stack or the Heap has a profound impact on performance.
The Two Main Memory Areas
The Stack:
- Analogy: A stack of plates. You can only add a new plate to the top or remove the top plate. It's very fast and simple.
- Characteristics:
- Allocating and freeing memory is extremely fast (it's just moving a pointer).
- It stores Value Types:
int,double,bool,DateTime, and most importantly,structs. - Memory is automatically cleaned up when a method finishes.
The Heap:
- Analogy: A large, messy warehouse. When you need space, the manager (the Garbage Collector) has to find a big enough empty spot.
- Characteristics:
- Allocating memory is slower.
- It stores Reference Types:
string,object, arrays ([]), and instances ofclasses. - Memory is managed by the Garbage Collector (GC). The GC must periodically run to find and clean up objects that are no longer in use, which costs CPU time.
struct vs. class: The Performance Battle
This fundamental difference leads to significant performance consequences.
class(Reference Type):csharpvar myObject = new MyClass();- The data for
myObjectis created on the Heap. - The
myObjectvariable on the Stack is just a small pointer to the memory location on the Heap. - Cost: Heap allocation + future pressure on the GC.
- The data for
struct(Value Type):csharpvar myValue = new MyStruct();- All the data for
myValueis created directly on the Stack (if it's a local variable). - The
myValuevariable is the data. - Benefit: No Heap allocation, no garbage for the GC. Extremely fast.
- All the data for
When Should You Use a struct for Optimization?
Even though structs are fast, they aren't always the best choice because they are copied when passed between methods.
The Golden Rule: Use a struct when your object is:
- Small: Ideally under 16 bytes.
- Immutable: Its fields do not change after it's created.
- Represents a single value: For example, a
Point(x, y coordinates), aColor(R, G, B, A), or aKeyValuePair.
Examples:
// GOOD: Use a struct for a 2D point.
// It's small and represents a single value.
public readonly struct Point
{
public double X { get; }
public double Y { get; }
// ... constructor
}
// BAD: Use a struct for a complex object.
// It's large, and copying it would be expensive.
public struct Person
{
public string FirstName; // string is a reference type
public string LastName;
public int Age;
// ...
}Conclusion:
In "hot paths" (code that runs very frequently), replacing small classes with structs can significantly reduce pressure on the Garbage Collector. This leads to lower CPU usage and eliminates the "pauses" caused by the GC, making your application run smoother and more consistently.