Я знаю, как это предотвратить
Я просто не знаю, почему это происходит (пока).И может показаться, что вы действительно нашли ошибку либо в .Net BCL, либо, что более вероятно, в JIT.
Я просто закомментировал все строки в методе MyButton_Click_Aux
, а затем начал возвращать их обратно.в, один за другим.
Снимите volatile
со статического int, и вы больше не получите 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);
//}
}
Мне также было интересно, было ли это как-то связано с неинициализированным reportCount
при попытке /наконец-то в. Но не имеет значения, если вы инициализируете его в ноль.
Я ищусейчас в IL - хотя для этого может потребоваться кто-то с некоторыми ASM-партнерами ...
Окончательное обновление Как я уже сказал, для этого действительно потребуется анализ выходных данных JIT длядействительно понимаю, что происходит, и хотя мне интересно анализировать ассемблер - я чувствую, что это, возможно, работа для кого-то в Microsoft, так что эту ошибку можно фактически подтвердить и исправить!Тем не менее, это кажется довольно узким набором обстоятельств.
Я перешел к сборке релиза, чтобы избавиться от всего шума 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
разница?Ну, есть два, которые я заметил - бокс 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);
}
Упс - StackOverflowException
.
И:
try { }
finally
{
var doesNothing = new DoesNothing();
doesNothing.NonVirtualFooBox(reportCount);
}
Упс снова!StackOverflowException
!
Мы могли бы пойти дальше с этим - но я чувствую, что проблема явно связана с боксом volatile int, в то время как внутри блока finally try / catch ... Я помещаюКод внутри попробовать, и нет проблем.Я добавил предложение catch (и поместил туда код), также без проблем.
Это также может относиться к боксу других типов значений, я думаю.
Итак, для подведения итогов - в.Net 4.0 - как в сборках отладки, так и в выпуске - бокс volatile int в блоке finally, как представляется, заставляет JIT генерировать код, который в итоге заполняет стекТот факт, что трассировка стека просто показывает «внешний код», также поддерживает это предложение.
Существует даже вероятность того, что она не всегда может быть воспроизведена и может даже зависеть от компоновки и размера генерируемого кода.попробуй / наконец.Это явно связано с ошибкой jmp
или с созданием чего-то подобного в неправильном месте, которое в конечном итоге повторяет одну или несколько команд push в стек.Откровенно говоря, идея о том, что это вызвано на самом деле операцией с ящиком, захватывающая!
Окончательное финальное обновление
Если вы посмотрите на ошибку MS Connect, то @HastyG найдено (ответьте ниже) - вы видите, что ошибка проявляется похожим образом, но с изменчивым bool в выражении catch .
Также - MS поставила исправление в очередь после того, как запустила его для воспроизведения, но через 7 месяцев исправление недоступно До этого я записывался как участник поддержки MS Connect, поэтому больше ничего не скажу - не думаю, что мне нужно!
Окончательное окончательное окончательное обновление (23/02/2011)
Исправлено - но еще не выпущено. Цитата из MS Team об ошибке MS Connect:
Да, это исправлено. Мы находимся в процессе выяснения, как лучше всего отправить исправление. Это уже исправлено в 4.5, но мы действительно хотели бы исправить пакет ошибок генерации кода до выпуска 4.5.