Nik*_*tov 87 c# clr stack stack-size
今天的PC有大量的物理RAM,但是C#的堆栈大小对于32位进程只有1 MB而对于64位进程只有4 MB(C#中的堆栈容量).
为什么CLR中的堆栈大小仍然如此有限?
为什么它恰好是1 MB(4 MB)(而不是2 MB或512 KB)?为什么决定使用这些金额?
我对该决定背后的考虑因素和原因感兴趣.
Han*_*ant 185
你正在看那个做出这个选择的人.David Cutler和他的团队选择了一兆字节作为默认堆栈大小.与.NET或C#无关,这在创建Windows NT时已被确定.当程序的EXE头或CreateThread()winapi调用没有明确指定堆栈大小时,它会选择一兆字节.这是正常的方式,几乎所有程序员都将它留给操作系统来选择大小.
这个选择可能早于Windows NT设计,历史对此非常模糊.如果卡特勒会写一本关于它的书会很好,但他从未成为一名作家.他在计算机的工作方式上一直非常有影响力.他的第一个操作系统设计是RSX-11M,一个用于DEC计算机(Digital Equipment Corporation)的16位操作系统.它严重影响了Gary Kildall的CP/M,这是第一款适用于8位微处理器的体面操作系统.这严重影响了MS-DOS.
他的下一个设计是VMS,这是一个支持虚拟内存的32位处理器的操作系统.非常成功.在公司开始瓦解的时候,他的下一个被DEC取消,无法与廉价的PC硬件竞争.提示微软,他们向他提出了一个他无法拒绝的提议.他的许多同事也加入了.他们使用VMS v2,更好地称为Windows NT.DEC对此感到不安,钱转手解决它.VMS是否已经选择了一兆字节是我不知道的,我只知道RSX-11.这不太可能.
足够的历史.一兆字节很多,一个真正的线程很少消耗超过几千字节.所以一兆字节实际上相当浪费.然而,在需求分页的虚拟内存操作系统上,您可以承受的浪费,兆兆字节只是虚拟内存.只是处理器的数字,每4096字节一个.在实际解决之前,您实际上从未使用物理内存,即机器中的RAM.
它在.NET程序中过度使用,因为最初选择1兆字节大小以适应本机程序.这往往会创建大型堆栈帧,并在堆栈中存储字符串和缓冲区(数组).臭名昭着的恶意软件攻击向量,缓冲区溢出可以用数据操纵程序.不是.NET程序的工作方式,在GC堆上分配字符串和数组并检查索引.使用C#在堆栈上分配空间的唯一方法是使用不安全的stackalloc关键字.
.NET中唯一非常重要的堆栈用法是抖动.它使用您的线程堆栈即时编译MSIL到机器代码.我从来没有见过或检查它需要多少空间,它取决于代码的性质以及优化器是否启用,但几十千字节是一个粗略的猜测.除此之外,这个网站如何得名,.NET程序中的堆栈溢出是非常致命的.没有足够的空间(小于3千字节)仍然可靠JIT任何试图捕获异常的代码.Kaboom到桌面是唯一的选择.
最后但并非最不重要的是,.NET程序对堆栈做了一些相当无效的事情.CLR将提交一个线程的堆栈.这是一个昂贵的词,这意味着它不仅保留堆栈的大小,它还确保在操作系统的页面文件中保留空间,以便在必要时始终可以交换堆栈.未能提交是致命错误并无条件终止程序.这只发生在运行完全过多进程的RAM很少的机器上,这样的机器在程序开始死亡之前就会变成糖蜜.一个可能的问题15年前,而不是今天.调整程序以充当F1赛车的程序员<disableCommitThreadStack>
在其.config文件中使用该元素.
Fwiw,Cutler并没有停止设计操作系统.那张照片是他在Azure上工作时拍的.
更新,我注意到.NET不再提交堆栈.不完全确定何时或为何发生这种情况,自从我检查以来已经太久了.我猜这个设计更改发生在.NET 4.5的某个地方.非常明智的变化.
默认保留堆栈大小由链接器指定,开发人员可以通过在链接时更改 PE 值或通过指定WinAPI 函数的dwStackSize
参数为单个线程覆盖它CreateThread
。
如果您创建的线程的初始堆栈大小大于或等于默认堆栈大小,则它会向上舍入到最接近的 1 MB 倍数。
为什么该值对于 32 位进程等于 1 MB,对于 64 位进程等于 4 MB?我认为您应该询问设计 Windows 的开发人员,或者等到他们中的某个人回答您的问题。
可能 Mark Russinovich 知道这一点,你可以联系他。也许您可以在第六版之前的他的 Windows Internals 书籍中找到此信息,该书籍描述的有关堆栈的信息较少,而不是他的文章。或者也许 Raymond Chen 知道原因,因为他写了有关 Windows 内部及其历史的有趣内容。他也可以回答您的问题,但您应该在意见箱 中提出建议。
但此时我将尝试使用 MSDN、Mark 和 Raymond 的博客来解释 Microsoft 选择这些值的一些可能原因。
默认值具有这些值可能是因为在早期 PC 很慢并且在堆栈上分配内存比在堆中分配内存要快得多。并且由于堆栈分配要便宜得多,因此使用它们,但它需要更大的堆栈大小。
因此,该值是大多数应用程序的最佳保留堆栈大小。这是最佳的,因为允许进行大量嵌套调用并在堆栈上分配内存以将结构传递给调用函数。同时它允许创建很多线程。
现在这些值主要用于向后兼容,因为作为参数传递给 WinAPI 函数的结构仍然在堆栈上分配。但是,如果您不使用堆栈分配,那么线程的堆栈使用量将显着小于默认的 1 MB,并且正如 Hans Passant 提到的那样浪费。并且为了防止这种情况,如果在应用程序的 PE 标头中未指定其他页面,则操作系统仅提交堆栈的第一页 (4 KB)。其他页面按需分配。
一些应用程序覆盖保留的地址空间并最初致力于优化内存使用。例如,IIS 本机进程线程的最大堆栈大小为 256 KB ( KB932909 )。微软建议降低默认值:
最好选择尽可能小的堆栈大小,并提交线程或光纤可靠运行所需的堆栈。为堆栈保留的每个页面都不能用于任何其他目的。
资料来源:
归档时间: |
|
查看次数: |
25229 次 |
最近记录: |