这是什么FatalExecutionEngineError在.NET 4.5测试版的原因是什么?

2019-06-26 21:51发布

下面的代码示例自然发生。 突然,我的代码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 ,或改变类型放慢参数objectint

我想我的笔记本电脑这个代码,并没有抱怨。 不过,我也尝试我的主要应用程序,它在笔记本电脑崩溃也是如此。 我必须有错位掉的东西减少了问题的时候,我会看看我是否能弄清楚那是什么。

我的笔记本电脑崩溃使用相同的代码上面,与框架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仍然存在。 - 韦恩

Answer 1:

这也不是一个完整的答案,但我有一些想法。

我相信我已经找到了良好的解释,我们会发现没有从.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的静态构造函数,代码永远不会失败。 挖掘到后TrimFormat ,很清楚的是,问题是, Length被内联的,并且这些样品上方的在String类型还没有被初始化。 特别是,在体内A的构造, string.Empty不正确地分配,虽然在身体内部Mainstring.Empty被正确分配。

令人吃惊的是我的类型的初始化String在某种程度上取决于是否A与值类型具体化。 我唯一的理论是,有针对的所有类型的共享泛型类型初始化一些优化JIT代码路径,而这条道路使有关BCL引用类型(“特殊类型?”)及其状态的假设。 就让我们来看看,虽然其他BCL班, public static字段显示,基本上所有的人实现一个静态构造函数(即使是空的构造函数和任何数据,像System.DBNullSystem.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.ctorB.Main()其中该IL用于第一版本包含:

newobj     instance void class A`1<object>::.ctor(string&)

... A`1<int32>...

在第二。

另一个要注意的是,对于JIT编译代码A<int>.ctor(out string) :是一样的,在非通用版本。



Answer 2:

我强烈怀疑这是造成这种优化(与BeforeFieldInit )在.NET 4.0中。

如果我没记错的话:

当你明确地声明静态构造函数, beforefieldinit发出,告诉静态构造函数必须在任何静态成员访问来运行的运行。

我猜:

我猜想,他们莫名其妙地搞砸了对64 JITer这个事实,所以, 不同类型的静态成员从一个类,其自身的静态构造函数已经运行的访问,它在某种程度上跳过运行(或以错误的顺序执行)的静态构造函数 - 因此导致系统崩溃。 (你没有得到一个空指针异常, 可能是因为它不是空初始化)。

我还没有遇到你的代码,所以这部分可能是错的-但如果我不得不做出另一种猜测,我会说这可能是这样string.Format (或Console.WriteLine ,这是类似),需要的是内部的访问导致崩溃,比如也许这需要显式的静态构造一个语言环境 -相关类。

同样,我没有测试它,但它是在数据我最好的猜测。

请随意测试我的假设,让我知道如何去。



Answer 3:

的观察,但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]
标签: c# clr