stackalloc是在栈上分配内存,返回Span且不经过GC;其生命周期限于当前方法,超量分配会直接引发无法捕获的StackOverflowException,禁止跨方法返回或存储。
stackalloc 在 C# 中用于在当前线程的栈上直接分配一块连续内存,返回 Span 或 ReadOnlySpan。它不经过 GC 管理,也不触发堆分配,所以速度快、无 GC 压力——但代价是:这块内存生命周期严格绑定于当前方法作用域,且必须在栈空间内完成分配。
常见误用是把它当成“轻量级堆替代品”,比如在循环里反复 stackalloc,或分配过大的块,结果不是性能提升,而是直接 栈溢出(StackOverflowException),且该异常无法被 try/catch 捕获。
默认线程栈大小在 Windows 上是 1MB(.NET 进程主线程),线程池线程通常也是 1MB;Linux/macOS 可能更小(如 512KB)。stackalloc 请求的字节数超过当前栈剩余空间时,运行时会立即抛出 StackOverflowException ——注意,这不是“接近耗尽”的警告,而是硬性失败。
stackalloc int[1024 * 1024] ≈ 4MB → 必然溢出stackalloc byte[8192](8KB)→ 一般安全,但若嵌套调用深(如递归 + 多层栈帧),仍可能踩线stackalloc MyStruct[100] 要按 Unsafe.SizeOf() 计算真实字节数
tch 中捕获 StackOverflowException.NET 不允许托管代码捕获 StackOverflowException(从 .NET Framework 2.0 起强制限制)。即使你写:
try
{
Span s = stackalloc int[1000000];
}
catch (StackOverflowException)
{
// 这段代码永远不会执行
} 程序会直接崩溃(进程终止),不会进入 catch 块。这是设计使然:栈已损坏,运行时无法保证异常处理本身的栈帧还能安全压入。
因此,预防是唯一手段:
stackalloc 的元素数量做静态上限检查(例如用 #if DEBUG 断言)stackalloc
ArrayPool.Shared.Rent() 替代大块临时缓冲区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 就可能让整个线程静默退出。