你正在看着那个做出这个选择的人。 David Cutler 和他的团队选择 1 MB 作为默认堆栈大小。与 .NET 或 C# 无关,这在他们创建 Windows NT 时就已经确定了。当程序的 EXE 标头或 CreateThread() winapi 调用未明确指定堆栈大小时,它会选择 1 MB。这是正常的方式,几乎所有程序员都会让操作系统来选择大小。
这个选择可能早于 Windows NT 设计,历史对此太模糊了。如果卡特勒能写一本关于这方面的书就好了,但他从来都不是作家。他对计算机的工作方式具有非凡的影响力。他的第一个操作系统设计是 RSX-11M,这是一个用于 DEC 计算机(数字设备公司)的 16 位操作系统。它严重影响了 Gary Kildall 的 CP/M,这是第一个像样的 8 位微处理器操作系统。这极大地影响了 MS-DOS。
他的下一个设计是 VMS,这是一个支持虚拟内存的 32 位处理器操作系统。非常成功。他的下一个项目在 DEC 开始解体时被取消,因为无法与廉价的 PC 硬件竞争。提示微软,他们向他提出了一个他无法拒绝的提议。他的许多同事也加入了。他们开发了 VMS v2,也就是众所周知的 Windows NT。 DEC对此感到不满,并通过金钱转手来解决此事。我不知道VMS是否已经选择了1兆字节,我只了解RSX-11。这并非不可能。
足够的历史了。一兆字节是一个lot,一个真正的线程很少消耗超过几千字节的资源。所以一兆字节实际上是相当浪费的。然而,在按需分页虚拟内存操作系统上,这是您可以承受的浪费,兆字节只是虚拟内存。只是处理器的编号,每 4096 字节一个。在实际寻址之前,您永远不会真正使用物理内存(机器中的 RAM)。
它在 .NET 程序中显得格外过多,因为最初选择 1 MB 大小是为了容纳本机程序。这往往会创建大型堆栈帧,并在堆栈上存储字符串和缓冲区(数组)。缓冲区溢出因作为恶意软件攻击媒介而臭名昭著,可以用数据操纵程序。 .NET 程序的工作方式不同,字符串和数组在 GC 堆上分配并检查索引。使用 C# 在堆栈上分配空间的唯一方法是使用不安全的堆栈分配关键词。
.NET 中堆栈的唯一重要用途是抖动。它使用线程堆栈将 MSIL 即时编译为机器代码。我从未见过或检查过它需要多少空间,这取决于代码的性质以及是否启用优化器,但粗略猜测几十千字节。这就是这个网站得名的原因,.NET 程序中的堆栈溢出是相当致命的。没有足够的空间(少于 3 KB)来仍然可靠地 JIT 尝试捕获异常的任何代码。 Kaboom 到桌面是唯一的选择。
最后但并非最不重要的一点是,.NET 程序对堆栈执行的操作效率很低。 CLR 将commit线程的堆栈。这是一个昂贵的词,意味着它不仅保留堆栈的大小,还确保在操作系统的分页文件中保留空间,以便在必要时始终可以交换堆栈。未能提交是致命错误并无条件终止程序。这种情况只发生在内存很少、运行太多进程的机器上,这样的机器在程序开始死亡之前就会变成糖蜜。 15 多年前可能存在的问题,而不是今天。程序员将他们的程序调整为像 F1 赛车一样使用<disableCommitThreadStack> https://msdn.microsoft.com/en-us/library/bb882564%28v=vs.110%29.aspx.config 文件中的元素。
Fwiw,卡特勒并没有停止设计操作系统。这张照片是他在 Azure 工作时拍摄的。
更新,我注意到.NET 不再提交堆栈。不完全确定何时或为何发生这种情况,距离我检查已经太久了。我猜测这个设计更改发生在 .NET 4.5 左右。相当明智的改变。