为什么C#中的堆栈大小恰好是1 MB?


102

当今的PC具有大量的物理RAM,但对于32位进程,C#的堆栈大小仅为1 MB,对于64位进程,C#的堆栈大小仅为4 MB(C#中的堆栈容量)。

为什么CLR中的堆栈大小仍然如此有限?

为什么是1 MB(4 MB)(而不是2 MB或512 KB)呢?为什么决定使用这些金额?

对该决定背后的考虑和原因感兴趣。


6
64位进程的默认堆栈大小为4MB,32位进程的默认堆栈大小为1MB。您可以通过更改其PE标头中的值修改主线程堆栈的大小。您还可以通过使用Thread构造函数的正确重载来指定堆栈大小。但是,这引出了一个问题,为什么您需要更大的堆栈?
Yuval Itzchakov 2015年

2
谢谢,编辑。:)问题不是关于如何使用更大的堆栈大小,而是为什么堆栈大小决定为1 MB(4 MB)
Nikolay Kostov 2015年

8
因为默认情况下,每个线程都会获得此堆栈大小,并且大多数线程不需要那么多。我刚刚启动了PC,系统目前运行1200个线程。现在做数学;)
Lucas Trzesniewski 2015年

2
@LucasTrzesniewski不仅如此,它在内存中必须具有传染性。请注意,堆栈的大小越大,您的进程可以在其虚拟地址空间中创建的线程就越少。
Yuval Itzchakov 2015年

不确定“恰好”为1 MB:在我的Windows 8.1上,.NET Core 3.1控制台应用程序具有1572864默认的字节堆栈大小(使用GetCurrentThreadStackLimits Win32 API检索)。没有StackOverflowException,我stackalloc大约可以1500000字节。
乔治Chakhidze

Answers:


210

在此处输入图片说明

您正在寻找做出这一选择的人。David Cutler和他的团队选择1兆字节作为默认堆栈大小。与.NET或C#无关,当他们创建Windows NT时就被钉住了。当程序的EXE标头或CreateThread()winapi调用未明确指定堆栈大小时,它将选择一个兆字节。这是正常的方式,几乎所有程序员都将其留给操作系统来选择大小。

这种选择可能早于Windows NT设计,对此,历史太模糊了。如果卡特勒会写一本关于它的书,那会很好,但他从来不是作家。他对计算机的工作方式产生了极大的影响。他的第一个OS设计是RSX-11M,这是一种用于DEC计算机(数字设备公司)的16位操作系统。它极大地影响了Gary Kildall的CP / M,这是第一个用于8位微处理器的体面操作系统。这对MS-DOS影响很大。

他的下一个设计是VMS,该操作系统是具有虚拟内存支持的32位处理器的操作系统。非常成功。在公司开始瓦解时,DEC取消了他的下一个产品,无法与廉价的PC硬件竞争。提示微软,他们向他提出了他无法拒绝的提议。他的许多同事也加入了。他们在VMS v2(即Windows NT)上工作。DEC对此不高兴,钱转手解决了。我不知道VMS是否已经选择了1兆字节,我只非常了解RSX-11。这不太可能。

足够的历史。一兆字节是很多,一个真正的线程很少消耗超过几千字节的内存。因此,兆字节实际上是相当浪费的。但是,在按需分页的虚拟内存操作系统上您可以承受这种浪费,兆字节只是虚拟内存。只是处理器的编号,每4096个字节一个。在实际寻址之前,您永远不会真正使用物理内存(机器中的RAM)。

在.NET程序中,这是多余的,因为最初选择1兆字节的大小来容纳本机程序。这往往会创建大型堆栈框架,并在堆栈上存储字符串和缓冲区(数组)。众所周知,缓冲区溢出是恶意软件的攻击媒介,它可以操纵程序处理数据。.NET程序的工作方式不行,字符串和数组在GC堆上分配,并检查索引。使用C#在堆栈上分配空间的唯一方法是使用不安全的stackalloc关键字。

.NET中堆栈的唯一重要用途是抖动。它使用线程堆栈将MSIL即时编译为机器代码。我从来没有看过或检查过它需要多少空间,它取决于代码的性质以及是否启用了优化器,但是大概需要几十千字节。否则,这就是该网站的名称,.NET程序中的堆栈溢出非常致命。没有足够的空间(少于3 KB)来仍然可靠地JIT任何试图捕获异常的代码。Kaboom到桌面是唯一的选择。

最后但并非最不重要的一点是,.NET程序在堆栈上的工作效率很低。CLR将提交线程的堆栈。这是一个昂贵的词,这意味着它不仅保留堆栈的大小,而且还确保在操作系统的页面文件中保留空间,以便在必要时始终可以换出堆栈。未能提交是致命错误,将无条件终止程序。只有在内存很少且完全运行太多进程的RAM上才发生这种情况,在程序开始死亡之前,这种机器将变成糖蜜。15年前而不是今天可能出现的问题。将其程序调整为类似于F1赛车的程序的程序员使用<disableCommitThreadStack>其.config文件中的元素。

Fwiw,Cutler并没有停止设计操作系统。那张照片是他在Azure上工作时拍摄的。


更新,我注意到.NET不再提交堆栈。不确定何时或为什么发生这种情况,自从我检查以来已经过了很长时间。我猜这种设计更改发生在.NET 4.5周围的某个地方。非常明智的变化。


3
发表评论The only way to allocate space on the stack with C# is with the unsafe stackalloc keyword.-局部变量(例如int在方法内部声明的变量)是否未存储在堆栈中?我认为他们是。
RBT

2
好。现在我知道,堆栈框架不是函数局部变量存储的唯一选择。它可以尽管在你的子弹点之一建议存储在堆栈帧。汉斯很有启发。非常感谢您撰写如此有见地的文章。坦白地说,堆栈对于一般编程来说是一个很大的抽象,只是为了避免不必要的复杂性。
RBT

@Hans的详细说明。我只是想知道maxStackSize线程的最小可能值是多少?我在[MSDN](msdn.microsoft.com/zh-cn/library/5cykbwz4 ( v=vs.110 ) .aspx)上找不到它。根据您的评论,似乎堆栈使用量绝对是最小的,我可以使用最小值来容纳最大可能的线程。谢谢。
MKR

1
@KFL:您可以通过尝试轻松地回答您的问题!
埃里克·利珀特

1
如果默认行为是不再犯堆栈,那么这个降价文件需要被编辑github.com/dotnet/docs/blob/master/docs/framework/...
约翰Stewien

5

默认保留的堆栈大小由链接器指定,开发人员可以通过更改链接时的PE值或为单个线程指定WinAPI函数的dwStackSize参数来覆盖默认保留的堆栈大小CreateThread

如果创建的线程的初始堆栈大小大于或等于默认堆栈大小,则将其舍入为最接近的1 MB倍数。

为什么对于32位进程,该值等于1 MB,对于64位,该值等于4 MB?我认为您应该问问设计Windows的开发人员,或者等到其中有人回答您的问题。

Mark Russinovich大概知道这一点,您可以联系。也许您可以在第六版之前的Windows Internals书籍中找到此信息,该书描述的堆栈信息比他的文章要少。或者,也许Raymond Chen知道原因,因为他撰写了有关Windows内部及其历史的有趣的东西。他也可以回答您的问题,但是您应该在“ 建议箱”中发布建议

但是目前,我将尝试解释一些可能的原因,为什么Microsoft使用MSDN,Mark's和Raymond的博客选择这些值。

默认值具有这些值,可能是因为早期PC速度较慢,并且在堆栈上分配内存比在堆中分配内存快得多。而且由于堆栈分配便宜得多,因此可以使用它们,但是它需要较大的堆栈大小。

因此,该值是大多数应用程序的最佳保留堆栈大小。这是最佳选择,因为它允许进行大量的嵌套调用并在堆栈上分配内存以将结构传递给调用函数。同时,它允许创建很多线程。

如今,这些值主要用于向后兼容,因为作为参数传递给WinAPI函数的结构仍然分配在堆栈上。但是,如果您不使用堆栈分配,那么线程的堆栈使用量将大大低于默认的1 MB,这很浪费Hans Passant所说的。为了防止这种情况,如果应用程序的PE标头中未指定其他内容,则操作系统仅提交栈的第一页(4 KB)。其他页面按需分配。

一些应用程序会覆盖保留的地址空间,并最初致力于优化内存使用。例如,IIS本机进程的线程的最大堆栈大小为256 KB(KB932909)。微软建议降低默认值:

最好选择尽可能小的堆栈大小,并提交线程或光纤可靠运行所需的堆栈。保留给堆栈的每个页面都不能用于任何其他目的。

资料来源:

  1. 线程堆栈大小(Microsoft Docs)
  2. 突破Windows的极限:进程和线程(Mark Russinovich)
  3. 默认情况下,在本机IIS进程中创建的线程的最大堆栈大小为256 KB(KB932909)

如果我想要更大的堆栈大小,可以对其进行设置(atalasoft.com/cs/blogs/rickm/archive/2008/04/22/…)。我想知道该决定背后的考虑因素和原因。
Nikolay Kostov 2015年

2
好的。现在我明白了:)默认堆栈大小应该是最佳的(请参阅@Lucas Trzesniewski注释),并且应四舍五入到分配粒度的最接近倍数。如果指定的堆栈大小大于默认堆栈大小,则将其舍入为最接近的1MB倍数。因此,Microsoft选择了此大小作为所有用户模式应用程序的默认堆栈大小。并且没有其他原因。
Yoh Deadfall'Feb 22'15

有资料吗?有文件吗?:)
Nikolay Kostov

@Yoh有趣的链接。您应该将其总结为答案。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.