Rom*_*kov 8 .net stack-overflow clr
据我所知,没有根本原因StackOverflowException可以解开.但事实确实如此.
线程堆栈的最大大小默认为1MB或4MB,具体取决于位数,但此空间仅保留.它没有被提交(因此只使用虚拟地址空间),直到实际需要那么多堆栈.
据我所知,为什么它无法捕获的基本思想是,到那时你已经耗尽了所有堆栈,因此无法真正执行除了为堆栈外条件非常精心设计的代码之外的任何东西.但这需要明显的解决方案:StackOverflowException 在我们耗尽所有堆栈空间之前抛出!
为什么不这样做?我能想到的唯一一个远程合理的原因是,决定额外的虚拟地址空间(这不是真正的内存使用,请注意!)不值得使这个异常捕获的好处.
还有其他问题我没有考虑过吗?
我觉得我不得不问,因为大多数答案在SO上解决这个问题似乎意味着不可能让它合理地工作,这就是.NET让它们无法捕捉的原因.
可捕捉变体的确切实现细节似乎并不重要,但这只是一个想法.首先,将默认保留堆栈大小加倍,但将异常设置为在1MB使用时触发.到目前为止,这与现有方法完全相同.但是当达到阈值时,抛出一个可捕获的异常,而不是抛出一个无法捕获的异常,同时将阈值增加到1.5MB.如果异常被捕获并且我们再次超过限制,则将其设置为1.75MB.然后是1.875MB.等等.每个嵌套和处理的异常都会获得不断增加的额外堆栈空间,直到我们接近2MB才能要求抛出一个无法捕获的变量.
为了在成功处理StackOverflowException之后减少阈值,让我们将内存页面标记为我们刚刚通过的堆栈大小的一半(因此在第一个实例中为0.5MB),以便在写入时出错.当我们回到堆栈使用水平时,故障处理程序将被完全触发,因此它并不昂贵.处理程序将检查实际的堆栈大小,并在适当时将阈值降低.
我不知道为什么它从 1.1 更改为 2.0(希望 Eric 能在某个时间发表评论),但我怀疑是因为这样的情况:
public bool Count(int64 i)
{
try
{
// somewhere in here a stack overflow exception is thrown
if(i < int64.MaxValue)
return Count(i+1);
else
return true;
} Catch(StackOverflowException soEx)
{
HandleError(soEx);
}
}
public void HandleError(Exception ex)
{
// error handling code here
}
Run Code Online (Sandbox Code Playgroud)
实际上,您也会在您的行中抛出异常,HandleError(soEx);因为它在堆栈太满之前停止了。
我怀疑这就是他们选择使堆栈溢出异常不可捕获的原因,因为您无法捕获它然后调用该 catch 块中的任何方法。
至于你的建议,他们不想有一个堆栈大小和一个有效的堆栈大小,因为这会让事情变得复杂,我怀疑他们也不想有一个可变的堆栈大小。这些事情中的任何一个都会使框架变得过于复杂,并且只提供部分解决方案,出于争论的目的,如果您的代码是:
public void HandleError(Exception ex)
{
try
{
HandleError(soEx);
} Catch(StackOverflowException soEx)
{
HandleError(soEx);
}
}
Run Code Online (Sandbox Code Playgroud)
使用变量堆栈并且无法实际阻止 StackOverflowException 被捕获,这会导致其他问题崩溃。
因此,将其扔出并且无法接住会更容易且更可预测。
编辑:这个答案指出了在 CLR 2.0+ 中仍然可以捕获 StackOverflowExceptions 的唯一情况
| 归档时间: |
|
| 查看次数: |
385 次 |
| 最近记录: |