stackalloc是在栈上分配内存,返回Span且不经过GC;其生命周期限于当前方法,超量分配会直接引发无法捕获的StackOverflowException,禁止跨方法返回或存储。

stackalloc 是在栈上分配内存,不是堆
stackalloc 在 C# 中用于在当前线程的栈上直接分配一块连续内存,返回 Span 或 ReadOnlySpan。它不经过 GC 管理,也不触发堆分配,所以速度快、无 GC 压力——但代价是:这块内存生命周期严格绑定于当前方法作用域,且必须在栈空间内完成分配。
常见误用是把它当成“轻量级堆替代品”,比如在循环里反复 stackalloc,或分配过大的块,结果不是性能提升,而是直接 栈溢出(StackOverflowException),且该异常无法被 try/catch 捕获。
stackalloc 分配大小受栈剩余空间限制
默认线程栈大小在 Windows 上是 1MB(.NET 进程主线程),线程池线程通常也是 1MB;Linux/macOS 可能更小(如 512KB)。stackalloc 请求的字节数超过当前栈剩余空间时,运行时会立即抛出 StackOverflowException ——注意,这不是“接近耗尽”的警告,而是硬性失败。
-
stackalloc int[1024 * 1024]≈ 4MB → 必然溢出 -
stackalloc byte[8192](8KB)→ 一般安全,但若嵌套调用深(如递归 + 多层栈帧),仍可能踩线 - 结构体大小影响显著:
stackalloc MyStruct[100]要按Unsafe.SizeOf计算真实字节数()
不能在 try/catch 中捕获 StackOverflowException
.NET 不允许托管代码捕获 StackOverflowException(从 .NET Framework 2.0 起强制限制)。即使你写:
try
{
Span s = stackalloc int[1000000];
}
catch (StackOverflowException)
{
// 这段代码永远不会执行
} 程序会直接崩溃(进程终止),不会进入 catch 块。这是设计使然:栈已损坏,运行时无法保证异常处理本身的栈帧还能安全压入。
因此,预防是唯一手段:
- 永远对
stackalloc的元素数量做静态上限检查(例如用#if DEBUG断言) - 避免在 public API 或不确定输入规模的路径中使用
stackalloc - 优先考虑
ArrayPool替代大块临时缓冲区.Shared.Rent()
stackalloc 不能跨方法返回或存储到字段
stackalloc 返回的是栈上地址,一旦方法返回,该地址就失效。编译器会阻止你把它赋给类字段、静态变量,或作为 ref/out 参数传出(除非用 ref struct 且严格限定生命周期)。
以下写法会被 C# 编译器拒绝:
private Span_buffer; // ❌ 字段类型不能是 Span (ref struct) Span
GetBuffer() { return stackalloc byte[256]; // ❌ 编译错误:Cannot return a stackalloc expression }
真正安全的用法只有一种:在单个方法体内分配、使用、结束——中间不逃逸,不延长生命周期。哪怕只是传给一个接受 Span 的本地函数,也必须确保该函数不保存引用。
栈空间不是可伸缩资源,stackalloc 的“快”是以确定性为前提的。越想省事越容易崩,尤其在高并发或深度调用场景下,一个没算准的 stackalloc 就可能让整个线程静默退出。










