线程栈超出分配限制时会怎样?为何我的.NET Core程序未报错?
线程栈分配限制疑问
我尝试理解线程栈分配限制,通过ulimit -s 50将栈限制设置为50KB后,运行了以下C#程序:
namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { var thread = new Thread(() => { DownloadImage(); }); thread.Start(); Console.ReadKey(); } private static async void DownloadImage() { using var client = new HttpClient(); using var file = await client.GetStreamAsync("some url with 1 MB file", CancellationToken.None); using var memoryStream = new MemoryStream(); await file.CopyToAsync(memoryStream); var memoryBytes = memoryStream.ToArray(); Console.WriteLine($"File has {memoryBytes.Length} bytes."); } } }
原本以为memoryBytes变量会占用线程栈的1MB内存,触发栈溢出异常,但程序运行正常,没有抛出异常。这是为什么?是不是.NET Core在运行时修改了该限制?
问题解析
memoryBytes的实际存储位置不在栈上:memoryBytes是字节数组,属于引用类型,它的数据内容存储在托管堆中,线程栈上仅保存了指向这个数组的引用(64位系统占8字节,32位系统占4字节),这点内存远低于50KB的栈限制,自然不会触发溢出。异步方法的执行逻辑不依赖原线程栈:
DownloadImage是异步方法,执行到await关键字时,当前线程会被释放回线程池;后续IO操作完成后,回调逻辑会由线程池重新调度其他线程执行,整个过程和你创建的那个线程的栈没有关联,不会占用它的栈空间。.NET不会修改系统栈限制:你通过
ulimit设置的50KB栈限制是有效的,但.NET的内存管理规则中,栈仅用于存储方法调用栈帧、值类型局部变量、引用类型的引用,像大数组、对象实例这类大块内存都会分配在托管堆上,和栈限制无关。
内容的提问来源于stack exchange,提问作者justin
相关产品推荐
相关产品推荐

