运行以下代码时,我得到StackOverflowException
:
private void MyButton_Click(object sender, EventArgs e) {
MyButton_Click_Aux();
}
private static volatile int reportCount;
private static void MyButton_Click_Aux() {
try { /*remove because stack overflows without*/ }
finally {
var myLogData = new ArrayList();
myLogData.Add(reportCount);
myLogData.Add("method MyButtonClickAux");
Log(myLogData);
}
}
private static void Log(object logData) {
// my log code is not matter
}
可能导致StackOverflowException
?
答案 0 :(得分:44)
我知道如何阻止它发生
我只是不知道为什么会导致它(还)。看起来你确实在.Net BCL中发现了一个错误,或者更可能发现在JIT中。
我刚评论了MyButton_Click_Aux
方法中的所有行,然后开始逐个将它们重新输入。
从静态int中取消volatile
,您将不再获得StackOverflowException
。
现在研究为什么......显然,与内存障碍有关的问题导致了一个问题 - 也许某种程度上迫使MyButton_Click_Aux
方法自称......
<强>更新强>
好的,其他人发现.Net 3.5不是问题。
我正在使用.Nt 4,所以这些评论与此相关:
正如我所说的那样,把挥发性物质关掉它就可以了。
同样,如果你重新启用volatile并删除try / finally它也可以:
private static void MyButton_Click_Aux()
{
//try { /*remove because stack overflows without*/ }
//finally
//{
var myLogData = new ArrayList();
myLogData.Add(reportCount);
//myLogData.Add("method MyButtonClickAux");
//Log(myLogData);
//}
}
我还想知道当try / finally进入时,是否与未初始化的reportCount
有关。但是如果你把它初始化为零则没有任何区别。
我现在正在看IL - 虽然它可能需要有一些ASM人员参与......
最终更新 正如我所说,这真的需要分析JIT输出才能真正理解发生了什么,同时我发现分析汇编程序很有趣 - 我觉得这可能是微软某人的工作,所以这个错误实际上可以被确认和修复!这就是说 - 它似乎是一个相当狭窄的环境。
我已经转移到发布版本以摆脱所有IL噪声(nops等)进行分析。
然而,这对诊断产生了复杂的影响。我以为我有它,但没有 - 但现在我知道它是什么。我试过这段代码:
private static void MyButton_Click_Aux()
{
try { }
finally
{
var myLogData = new ArrayList();
Console.WriteLine(reportCount);
//myLogData.Add("method MyButtonClickAux");
//Log(myLogData);
}
}
将int作为volatile。它运行没有错。这是IL:
.maxstack 1
L_0000: leave.s L_0015
L_0002: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
L_0007: pop
L_0008: volatile.
L_000a: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) WindowsFormsApplication1.Form1::reportCount
L_000f: call void [mscorlib]System.Console::WriteLine(int32)
L_0014: endfinally
L_0015: ret
.try L_0000 to L_0002 finally handler L_0002 to L_0015
然后我们查看再次获取错误所需的最小代码:
private static void MyButton_Click_Aux()
{
try { }
finally
{
var myLogData = new ArrayList();
myLogData.Add(reportCount);
}
}
这是IL:
.maxstack 2
.locals init (
[0] class [mscorlib]System.Collections.ArrayList myLogData)
L_0000: leave.s L_001c
L_0002: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
L_0007: stloc.0
L_0008: ldloc.0
L_0009: volatile.
L_000b: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) WindowsFormsApplication1.Form1::reportCount
L_0010: box int32
L_0015: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
L_001a: pop
L_001b: endfinally
L_001c: ret
.try L_0000 to L_0002 finally handler L_0002 to L_001c
区别?那么我发现了两个 - 挥动int的装箱和虚拟通话。所以我设置了这两个类:
public class DoesNothingBase
{
public void NonVirtualFooBox(object arg) { }
public void NonVirtualFooNonBox(int arg) { }
public virtual void FooBox(object arg) { }
public virtual void FooNonBox(int arg) { }
}
public class DoesNothing : DoesNothingBase
{
public override void FooBox(object arg) { }
public override void FooNonBox(int arg) { }
}
然后尝试了这四种版本的违规方法:
try { }
finally
{
var doesNothing = new DoesNothing();
doesNothing.FooNonBox(reportCount);
}
哪个有效。
try { }
finally
{
var doesNothing = new DoesNothing();
doesNothing.NonVirtualFooNonBox(reportCount);
}
哪个也有效。
try { }
finally
{
var doesNothing = new DoesNothing();
doesNothing.FooBox(reportCount);
}
糟糕 - StackOverflowException
。
和
try { }
finally
{
var doesNothing = new DoesNothing();
doesNothing.NonVirtualFooBox(reportCount);
}
再次哎呀! StackOverflowException
!
我们可以更进一步 - 但问题是,我觉得,显然是由于在try / catch的finally块内部的volatile int的装箱...我将代码置于try中,并且没有问题。我添加了一个catch子句(并将代码放在那里),也没问题。
它也可以应用于我猜的其他值类型的装箱。
因此,总结一下 - 在.Net 4.0中 - 在调试和发布版本中 - 在finally块中装入volatile int似乎会导致JIT生成最终填满堆栈的代码。堆栈跟踪只显示“外部代码”这一事实也支持这一主张。
甚至有可能不能总是复制它,甚至可能取决于try / finally生成的代码的布局和大小。这显然与错误的jmp
或类似的东西生成到错误的位置有关,最终会向堆栈重复一个或多个推送命令。坦率地说,这实际上是由盒子操作引起的这个想法很有意思!
最终的最终更新
如果你看看@Hasty G找到的MS Connect错误(进一步回答) - 你会看到bug以类似的方式表现出来,但在 catch 语句中有一个易变的bool
此外 - MS在重新启动后排队修复此问题 - 但7个月后还没有可用的修补程序。我之前已经记录在支持MS Connect,所以我不会再说了 - 我认为我不需要!
最终决赛最终更新(2011年2月23日)
它是固定的 - 但尚未发布。来自MS Team的MS Connect错误:
是的,它是固定的。我们正在弄清楚如何最好地发布修复程序。它已在4.5中修复,但我们真的想在4.5版本之前修复一批代码生成错误。
答案 1 :(得分:12)
该错误存在于您的代码中。据推测,MyButton_Click_Aux()
会导致某些方法被重新输入。但是,你已经莫名其妙地从你的问题中省略了该代码,因此没有人可以对它进行评论。
答案 2 :(得分:0)
Log会回拨给Log吗?这也会导致SO。
答案 3 :(得分:0)
当发生异常时,为什么不检查调用堆栈面板中记录的内容?调用堆栈本身可以说明很多。
此外,使用SOS.dll和WinDbg的低级调试也可以告诉你很多。