求C#中Stack与Heap性能差异的代码示例及研究资料
Stack与Heap性能对比的C#代码示例
一、核心差异说明
- Stack:用于存储值类型(如
int、自定义struct),内存分配/释放仅通过栈指针移动完成,速度极快,但空间有限,由CLR自动管理无需GC介入。 - Heap:用于存储引用类型(如
class、数组),内存分配需查找可用内存块,释放依赖GC回收,速度较慢,但存储空间充裕。
二、性能测试代码
以下代码通过大规模迭代创建值类型(Stack分配)和引用类型(Heap分配),统计两者的耗时差异,同时加入GC回收的额外开销测试:
using System; using System.Diagnostics; namespace StackHeapBenchmark { // 栈存储的值类型 public struct StackValue { public int Id; public string Label; // 注:string为引用类型,实际存储在Heap,但struct实例本身在Stack } // 堆存储的引用类型 public class HeapObject { public int Id; public string Label; } class Program { static void Main(string[] args) { const long TestCount = 10_000_000; // 测试迭代次数 var stopwatch = new Stopwatch(); // 测试Stack分配性能 stopwatch.Start(); for (long i = 0; i < TestCount; i++) { var stackItem = new StackValue { Id = (int)i, Label = $"Stack_{i}" }; // 保留简单操作避免编译器优化 if (stackItem.Id == TestCount) Console.WriteLine(stackItem.Label); } stopwatch.Stop(); Console.WriteLine($"Stack 分配耗时: {stopwatch.ElapsedMilliseconds} ms"); // 测试Heap分配性能 stopwatch.Reset(); stopwatch.Start(); for (long i = 0; i < TestCount; i++) { var heapItem = new HeapObject { Id = (int)i, Label = $"Heap_{i}" }; // 保留简单操作避免编译器优化 if (heapItem.Id == TestCount) Console.WriteLine(heapItem.Label); } stopwatch.Stop(); Console.WriteLine($"Heap 分配耗时: {stopwatch.ElapsedMilliseconds} ms"); // 测试Heap分配+GC回收的总开销 stopwatch.Reset(); stopwatch.Start(); for (long i = 0; i < TestCount; i++) { var heapItem = new HeapObject { Id = (int)i, Label = $"Heap_GC_{i}" }; } GC.Collect(); // 手动触发GC,模拟内存回收的额外开销 stopwatch.Stop(); Console.WriteLine($"Heap 分配+GC回收耗时: {stopwatch.ElapsedMilliseconds} ms"); } } }
三、测试结果解读
- 正常情况下,Stack分配的耗时会远低于Heap分配,因为Stack内存操作仅为栈指针的上下移动,无需复杂的内存寻址和对象标记。
- 当加入GC回收操作后,Heap的总耗时会显著增加——GC需要暂停线程、标记存活对象、清理内存碎片,这些操作都会带来额外性能开销。
四、测试注意事项
- 编译器可能会对无意义的对象创建进行优化,因此代码中保留了简单的条件判断,避免测试结果失真。
- 测试结果受硬件配置、CLR版本、系统负载影响,建议多次测试取平均值。
内容的提问来源于stack exchange,提问作者Samiullah Hafiz
相关产品推荐
相关产品推荐

