Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在.NET中查找StackOverflowException的原因?_C#_.net_Winforms_Volatile_Stack Overflow - Fatal编程技术网

C# 如何在.NET中查找StackOverflowException的原因?

C# 如何在.NET中查找StackOverflowException的原因?,c#,.net,winforms,volatile,stack-overflow,C#,.net,Winforms,Volatile,Stack Overflow,当我运行以下代码时,我得到一个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

当我运行以下代码时,我得到一个
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的原因是什么?

错误在代码中。大概,
MyButton\u Click\u Aux()
会导致重新输入某些方法。然而,您在问题中莫名其妙地忽略了该代码,因此没有人可以对此进行评论。

Log是否会调用Log?这也会导致SO。

我知道如何阻止它发生

我只是不知道为什么会这样。看起来您确实在.Net BCL中或者更可能在JIT中发现了一个bug

我刚刚注释掉了
MyButton\u Click\u Aux
方法中的所有行,然后开始一行一行地把它们带回来

从static int中去掉
volatile
,您将不再获得
堆栈溢出异常

现在来研究一下为什么。。。很明显,与内存障碍有关的东西正在引发一个问题——也许是某种原因迫使
MyButton\u Click\u Aux
方法调用自己

更新

好吧,其他人发现.NET3.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噪声(NOP等)进行分析

然而,这对诊断产生了复杂的影响。我以为我有,但没有,但现在我知道它是什么了

我尝试了以下代码:

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
区别是什么?我发现了两个——volatile 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);
}
Oops-
StackOverflowException

以及:

哎呀<代码>堆栈溢出异常

我们可以更进一步,但我觉得问题显然是由于在try/catch的finally块中对volatile int进行装箱引起的。。。我把代码放在try中,没有问题。我添加了一个catch子句(并将代码放在其中),也没有问题

我想它也可以应用于其他值类型的装箱

因此,总结一下,在.Net 4.0中,在调试和发布版本中,在finally块中装箱一个volatile int似乎会导致JIT生成最终填充堆栈的代码。堆栈跟踪仅显示“外部代码”这一事实也支持这一观点

甚至有一种可能性,它不能总是被复制,甚至可能取决于try/finally生成的代码的布局和大小。很明显,这与错误的
jmp
或类似的东西生成到错误的位置有关,最终会向堆栈重复一个或多个push命令。坦率地说,这种现象实际上是由盒子操作引起的,这是一种迷人的想法

最终更新

如果你看一下@Hasty G发现的MS-Connect错误(再往下回答),你会发现这个错误以类似的方式出现,但是在catch语句中有一个不稳定的bool

另外,MS在将其提交给Repo后排队等待修复程序,但7个月后还没有可用的热修复程序。我之前已经公开表示支持MS Connect,所以我不再多说了——我认为我不需要这样做

最终更新(2011年2月23日)

它是固定的,但尚未发布。引用MS团队关于MS Connect错误的话:

是的,已经修好了。我们正在研究如何最好地发布修复程序。它已经在4.5版本中修复,但我们确实希望在4.5版本之前修复一批代码生成错误


当异常发生时,为什么不检查调用堆栈面板中记录的内容?调用堆栈本身可以说明很多问题


此外,使用SOS.dll和WinDbg进行低级别调试也可以告诉您很多信息。

“因此它一定是.net中的bug”可能不是。请发布
MyButton\u Click\u Aux
的代码,请在MyButton\u Click\u Aux中包含代码。顺便说一句,我应该指出,您无法捕获
StackOverflowException
StackOverflowException
是一种致命的情况,因为嵌套调用太多,导致执行堆栈崩溃。这意味着你的程序已经病入膏肓,应该拔掉插头。CLR就是这么做的,它会杀死你的进程。-9?他只是在寻求帮助@Jonathan Wood我没有复述我知道递归我有很多c编码经验这是我认为必须的形式bug@PRASHANT:不,你还没有确定。我很高兴您更新了问题中的代码,但是
try
块中没有显示的是什么?我是否从注释中了解到丢失的代码是导致溢出的原因?无论如何,VisualStudio有一个很棒的调试器。逐步完成代码。然后可以确定f
try { }
finally
{
  var doesNothing = new DoesNothing();
  doesNothing.NonVirtualFooBox(reportCount);
}