据我所知,没有根本原因StackOverflowException
应该是无法捕获的。然而它确实如此。
线程堆栈的最大大小默认为 1MB 或 4MB,具体取决于位数,但该空间仅保留。在实际需要那么多堆栈之前,它不会被提交(因此只使用虚拟地址空间)。
据我所知,为什么它无法捕获背后的基本想法是,到那时你已经用完了所有堆栈,因此除了为堆栈外条件精心设计的代码之外,无法真正执行任何东西。但这需要一个明显的解决方案:在我们用完所有堆栈空间StackOverflowException
之前抛出!
为什么不这样做?我能想到的唯一远程合理的原因是,决定这将消耗的额外虚拟地址空间(不是真正的内存使用,请注意!)不值得让这个异常可捕获。
还有其他我没有考虑的问题吗?
我觉得我不得不问,因为大多数解决这个问题的答案似乎都暗示不可能让它正常工作,这就是.NET 让它们无法捕获的原因。
可捕获变体的确切实现细节似乎并不重要,但这只是一个想法。首先,将默认保留堆栈大小加倍,但将异常设置为在 1MB 使用时触发。到目前为止,这与现有方法完全相同。但是当达到阈值时,不要抛出无法捕获的异常,而是抛出一个可捕获的异常,同时将阈值增加到 1.5MB。如果捕获到异常并且我们再次突破限制,请将其设置为 1.75MB。然后是 1.875MB。等等。每个嵌套和处理的异常都会获得越来越少的额外堆栈空间来处理,直到我们接近 2MB 以要求抛出无法捕获的变体。
为了在成功处理 StackOverflowException 后降低阈值,让我们将内存页面标记为我们刚刚通过的堆栈大小的一半(在第一个实例中为 0.5MB)以在写入时出错。当我们回到堆栈使用水平时,故障处理程序将几乎完全被触发,因此它并不昂贵。处理程序将检查实际堆栈大小并在适当时将阈值放回原处。