下面的代码示例自然发生。 突然,我的代码thew一个非常讨厌的冠冕堂皇的FatalExecutionEngineError
例外。 我花了好30分钟试图隔离和减少的罪魁祸首样品。 使用Visual Studio 2012作为一个控制台应用程序编译如下:
class A<T>
{
static A() { }
public A() { string.Format("{0}", string.Empty); }
}
class B
{
static void Main() { new A<object>(); }
}
应该产生在NET框架4和4.5此错误:
这是一个已知的错误,是什么原因,我能做些什么来缓解呢? 我目前的解决办法是不使用string.Empty
,但我在找错了树? 更改有关代码什么让你所期望它的功能-例如去除的空静态构造A
,或改变类型放慢参数object
到int
。
我想我的笔记本电脑这个代码,并没有抱怨。 不过,我也尝试我的主要应用程序,它在笔记本电脑崩溃也是如此。 我必须有错位掉的东西减少了问题的时候,我会看看我是否能弄清楚那是什么。
我的笔记本电脑崩溃使用相同的代码上面,与框架4.0,但主要的崩溃甚至4.5。 这两种系统都采用了最新更新VS'12(七月?)。
更多信息 :
- IL代码(编译调试/任何CPU / 4.0 / VS2010(不是IDE应该的问题)?): http://codepad.org/boZDd98E
- 没有看到VS 2010年4.0。 与不崩溃/无优化,不同的目标CPU,调试器附接/未连接,等等- 添梅多拉
- 在2010年崩溃,如果我用AnyCPU,在x86的罚款。 坠毁在Visual Studio 2010 SP1,使用平台目标= AnyCPU,但细跟平台目标= 86。 本机具有VS2012RC安装以及因此可能4.5做就地更换。 使用AnyCPU和上投放= 3.5那么它不会崩溃所以看起来像在Framework.-回归colinsmith
- 不能在x86,x64的或AnyCPU在VS2010与4.0重现。 - 富士
- 仅发生于64,(2012rc,Fx4.5) - 亨克Holterman
- VS2012 RC上的Win8 RP。 针对.NET 4.5,当最初没有看到这个MDA。 当切换到目标.NET 4.0的MDA出现。 然后切换回.NET 4.5后MDA仍然存在。 - 韦恩
这也不是一个完整的答案,但我有一些想法。
我相信我已经找到了良好的解释,我们会发现没有从.NET JIT团队回答别人。
UPDATE
我看了看深入一点,我相信我已经找到了问题的根源。 它似乎是由在JIT类型初始化逻辑的一个错误的组合引起的,并在C#编译器依赖于该JIT按预期工作的假设的变化。 我认为JIT漏洞存在于.NET 4.0,但被用于.NET 4.5的编译器的变化发现。
我不认为beforefieldinit
是唯一的问题在这里。 我认为这是简单得多。
类型System.String
在mscorlib.dll从.NET 4.0中包含一个静态构造函数:
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr ""
IL_0005: stsfld string System.String::Empty
IL_000a: ret
} // end of method String::.cctor
在mscorlib.dll的.NET 4.5版本, String.cctor
(静态构造函数)是特别显眼:
.....没有静态构造函数:( .....
在这两个版本的String
类型,装饰着beforefieldinit
:
.class public auto ansi serializable sealed beforefieldinit System.String
我试图创建一个类型,将编译成IL相似(使之具有静态字段,但没有静态构造函数.cctor
),但我不能这样做。 所有这些类型的有.cctor
在IL方法:
public class MyString1 {
public static MyString1 Empty = new MyString1();
}
public class MyString2 {
public static MyString2 Empty = new MyString2();
static MyString2() {}
}
public class MyString3 {
public static MyString3 Empty;
static MyString3() { Empty = new MyString3(); }
}
我的猜测是,.NET 4.0和4.5之间变化两件事情:
第一:EE已更改,以便它会自动初始化String.Empty
从非托管代码。 这种变化可能是用于.NET 4.0制作。
第二:编译改变,因此它并没有发出字符串静态构造函数,知道String.Empty
会从非托管方进行分配。 这种变化出现.NET 4.5已经作出。
看来,EE 不分配String.Empty
沿着一定的优化路径,很快就好了。 改变编译器做(或任何改变,使String.cctor
消失)预计EE做这个任务的任何用户代码执行前,但现在看来,在EE不会使这个任务之前String.Empty
中的参考方法使用键入具体化泛型类。
最后,我认为,这个错误表示在JIT类型初始化逻辑更深层次问题。 它出现在编译器的变化是一种特殊情况下System.String
,但我怀疑的是,JIT已经在这里做了一个特殊的案例System.String
。
原版的
首先,WOW的BCL人已经得到了非常有创意的一些性能优化。 许多 String
方法现在使用线程静态缓存执行StringBuilder
对象。
我跟着铅了一段时间,但StringBuilder
是不是上使用的Trim
的代码路径,所以我决定它不可能是一个线程静态问题。
我想我发现了同样的错误的一个奇怪的表现虽然。
此代码失败,并访问冲突:
class A<T>
{
static A() { }
public A(out string s) {
s = string.Empty;
}
}
class B
{
static void Main() {
string s;
new A<object>(out s);
//new A<int>(out s);
System.Console.WriteLine(s.Length);
}
}
但是,如果取消注释//new A<int>(out s);
在Main
然后代码工作得很好。 事实上,如果A
是与任何引用类型物化,程序失败,但是如果A
与任何类型的值物化然后代码不会失败。 此外,如果你注释掉A
的静态构造函数,代码永远不会失败。 挖掘到后Trim
和Format
,很清楚的是,问题是, Length
被内联的,并且这些样品上方的在String
类型还没有被初始化。 特别是,在体内A
的构造, string.Empty
不正确地分配,虽然在身体内部Main
, string.Empty
被正确分配。
令人吃惊的是我的类型的初始化String
在某种程度上取决于是否A
与值类型具体化。 我唯一的理论是,有针对的所有类型的共享泛型类型初始化一些优化JIT代码路径,而这条道路使有关BCL引用类型(“特殊类型?”)及其状态的假设。 就让我们来看看,虽然其他BCL班, public static
字段显示,基本上所有的人实现一个静态构造函数(即使是空的构造函数和任何数据,像System.DBNull
和System.Empty
。BCL值类型的public static
似乎字段不实现一个静态构造函数( System.IntPtr
,例如)。这似乎表明,JIT使有关BCL引用类型初始化一些假设。
仅供参考下面是两个版本的JIT编译的代码:
A<object>.ctor(out string)
:
public A(out string s) {
00000000 push rbx
00000001 sub rsp,20h
00000005 mov rbx,rdx
00000008 lea rdx,[FFEE38D0h]
0000000f mov rcx,qword ptr [rcx]
00000012 call 000000005F7AB4A0
s = string.Empty;
00000017 mov rdx,qword ptr [FFEE38D0h]
0000001e mov rcx,rbx
00000021 call 000000005F661180
00000026 nop
00000027 add rsp,20h
0000002b pop rbx
0000002c ret
}
A<int32>.ctor(out string)
:
public A(out string s) {
00000000 sub rsp,28h
00000004 mov rax,rdx
s = string.Empty;
00000007 mov rdx,12353250h
00000011 mov rdx,qword ptr [rdx]
00000014 mov rcx,rax
00000017 call 000000005F691160
0000001c nop
0000001d add rsp,28h
00000021 ret
}
代码(其余Main
)是在两个版本之间是相同的。
编辑
此外,从所述两个版本的IL是除了调用相同A.ctor
在B.Main()
其中该IL用于第一版本包含:
newobj instance void class A`1<object>::.ctor(string&)
与
... A`1<int32>...
在第二。
另一个要注意的是,对于JIT编译代码A<int>.ctor(out string)
:是一样的,在非通用版本。
我强烈怀疑这是造成这种优化(与BeforeFieldInit
)在.NET 4.0中。
如果我没记错的话:
当你明确地声明静态构造函数, beforefieldinit
发出,告诉静态构造函数必须在任何静态成员访问来运行的运行。
我猜:
我猜想,他们莫名其妙地搞砸了对64 JITer这个事实,所以, 当不同类型的静态成员从一个类,其自身的静态构造函数已经运行的访问,它在某种程度上跳过运行(或以错误的顺序执行)的静态构造函数 - 因此导致系统崩溃。 (你没有得到一个空指针异常, 可能是因为它不是空初始化)。
我还没有遇到你的代码,所以这部分可能是错的-但如果我不得不做出另一种猜测,我会说这可能是这样string.Format
(或Console.WriteLine
,这是类似),需要的是内部的访问导致崩溃,比如也许这需要显式的静态构造一个语言环境 -相关类。
同样,我没有测试它,但它是在数据我最好的猜测。
请随意测试我的假设,让我知道如何去。
的观察,但DotPeek示出了反编译的String.Empty从而:
/// <summary>
/// Represents the empty string. This field is read-only.
/// </summary>
/// <filterpriority>1</filterpriority>
[__DynamicallyInvokable]
public static readonly string Empty;
internal sealed class __DynamicallyInvokableAttribute : Attribute
{
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public __DynamicallyInvokableAttribute()
{
}
}
如果我宣布我自己Empty
以同样的方式,只是没有属性,我不再得到MDA:
class A<T>
{
static readonly string Empty;
static A() { }
public A()
{
string.Format("{0}", Empty);
}
}
文章来源: What's the cause of this FatalExecutionEngineError in .NET 4.5 beta? [closed]