.NET 4.5 beta sürümünde bu FatalExecutionEngineError nedeni nedir? [kapalı]


150

Aşağıdaki örnek kod doğal olarak oluştu. Aniden kodum çok kötü bir FatalExecutionEngineErroristisna. Ben suçlu örneği izole etmek ve en aza indirmek için 30 dakika iyi bir süre geçirdim. Konsol uygulaması olarak Visual Studio 2012'yi kullanarak bunu derleyin:

class A<T>
{
    static A() { }

    public A() { string.Format("{0}", string.Empty); }
}

class B
{
    static void Main() { new A<object>(); }
}

Bu hatayı .NET framework 4 ve 4.5'de üretmelidir:

FatalExecutionException ekran görüntüsü

Bu bilinen bir hata mı, nedeni nedir ve hafifletmek için ne yapabilirim? Şu anki işim kullanmak değil string.Empty, ama yanlış ağacı havlıyor muyum? Örneğin boş statik yapıcı kaldırarak - bu kodu hakkında bir şey değiştirme beklediğiniz gibi işlev yapar A, ya gelen tip Paramtre değişen objectiçin int.

Bu kodu dizüstü bilgisayarımda denedim ve şikayet etmedi. Ancak, ana uygulamamı denedim ve dizüstü bilgisayarda da çöktü. Sorunu azaltırken bir şeyleri karıştırmış olmalıyım, bunun ne olduğunu anlayabileceğim.

Dizüstü bilgisayarım yukarıdaki 4.0 ile aynı kod ile çöktü, ancak 4.0 ile ana çöküyor. Her iki sistem de en son güncellemelerle (Temmuz?) VS'12 kullanıyor.

Daha fazla bilgi :

  • IL Kodu (derlenmiş Hata Ayıklama / Herhangi bir CPU / 4.0 / VS2010 (IDE önemli değil mi?)): Http://codepad.org/boZDd98E
  • 4.0 ile VS 2010 görülmedi. Optimizasyonlar olmadan /, farklı hedef CPU, ayıklayıcı ekli ile çökmesini değil / vb bağlı değil - Tim Medora
  • AnyCPU kullanırsam 2010'daki çökmeler x86'da iyi olur. Platform Hedefi = AnyCPU kullanan ancak Platform Hedefi = x86 ile iyi olan Visual Studio 2010 SP1'deki çökmeler. Bu makinede VS2012RC de yüklü, bu yüzden 4.5 muhtemelen yerinde değiştirme yapıyor. AnyCPU ve TargetPlatform = 3.5 kullanın, o zaman çökmez, bu yüzden Framework'te bir gerileme gibi görünür. - colinsmith
  • VS2010'da 4.0 ile x86, x64 veya AnyCPU üzerinde çoğaltılamaz. - Fuji
  • Sadece x64, (2012rc, Fx4.5) için olur - Henk Holterman
  • Win8 RP'de VS2012 RC. Başlangıçta .NET 4.5 hedeflerken bu MDA görmüyor. .NET 4.0 hedeflemeye geçtiğinde MDA belirdi. Sonra .NET 4.5'e döndükten sonra MDA kalır. - Wayne

Halka açık biriyle birlikte statik bir yapıcı yapabileceğinizi hiç bilmiyordum. Heck statik yapıcıların var olduğunu hiç bilmiyordum.
Cole Johnson

Bir fikrim var: çünkü B biraz statik bir sınıf olmak sadece statik Main ile bir sınıfa değiştiriyorsunuz?
Cole Johnson

@ChrisSinclair, sanmıyorum. Demek istediğim bu kodu dizüstü bilgisayarımda test ettim ve aynı sonuçları aldım.
Gleno

@ColeJohnson Evet IL, bariz yerler hariç herkese uyar. Burada c # derleyicisinde herhangi bir hata var gibi görünmüyor.
Michael Graczyk

14
Hem burada bildirdiği için orijinal postere hem de mükemmel analizi için Michael'a teşekkürler. CLR'deki meslektaşlarım, hatayı burada yeniden oluşturmaya çalıştı ve 64 bit CLR'nin "Sürüm Adayı" sürümünde çoğaltıldığını keşfetti, ancak bir dizi hata düzeltmesi olan son "Üretime Serbest Bırakıldı" sürümünde değil RC. (RTM sürümü 15 Ağustos 2012'de herkese açık olacak.) Bu nedenle, bunun burada bildirilen sorunla aynı olduğuna inanıyorlar: connect.microsoft.com/VisualStudio/feedback/details/737108/…
Eric Lippert

Yanıtlar:


114

Bu da tam bir cevap değil, ama birkaç fikrim var.

.NET JIT ekibinin cevabından biri olmadan bulacağımız kadar iyi bir açıklama bulduğuma inanıyorum.

GÜNCELLEME

Biraz daha derinlere baktım ve sorunun kaynağını bulduğuma inanıyorum. JIT türü başlatma mantığındaki bir hata ve C # derleyicisindeki JIT'in amaçlandığı gibi çalıştığı varsayımına dayanan bir değişiklikten kaynaklanıyor gibi görünüyor. JIT hata .NET 4.0 vardı, ama .NET 4.5 için derleyici değişikliği ile ortaya çıktığını düşünüyorum.

beforefieldinitBurada tek sorun olduğunu sanmıyorum . Bence bundan daha basit.

Tip System.String.NET 4.0 den mscorlib.dll statik yapıcı içerir:

.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 dosyasının .NET 4.5 sürümünde String.cctor(statik yapıcı) dikkat çekici bir şekilde yoktur:

..... Statik yapıcı yok :( .....

Her iki versiyonda da Stringtip ile süslenmiştir beforefieldinit:

.class public auto ansi serializable sealed beforefieldinit System.String

Benzer şekilde IL'ye derlenecek bir tür oluşturmaya çalıştım (böylece statik alanlar var, ancak statik yapıcı yok .cctor), ancak yapamadım. Tüm bu türlerin .cctorIL'de bir yöntemi vardır:

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(); } 
}

Benim tahminime göre .NET 4.0 ve 4.5 arasında iki şey değişti:

Birincisi: EE, String.Emptyyönetilmeyen koddan otomatik olarak başlatılacak şekilde değiştirildi . Bu değişiklik muhtemelen .NET 4.0 için yapılmıştır.

İkincisi: Derleyici String.Empty, yönetilmeyen taraftan atanacağını bilerek, dize için statik bir yapıcı yaymayacak şekilde değişti . Bu değişiklik .NET 4.5 için yapılmış gibi görünüyor.

O EE görünür değil atamak String.Emptybazı optimizasyon yollar boyunca çok yakında. Derleyicide yapılan değişiklik (veya String.cctorkaybolmak için değiştirilen her şey ), EE'nin bu atamayı herhangi bir kullanıcı kodu yürütülmeden yapmasını bekledi, ancak EE'nin bu atamayı daha önce String.Emptyreferans türü reified generic sınıflarında kullanılmadığı anlaşılıyor .

Son olarak, hatanın JIT tip başlatma mantığında daha derin bir sorunun göstergesi olduğuna inanıyorum. Derleyicideki değişiklik için özel bir durum olduğu anlaşılıyor System.String, ancak JIT'in burada özel bir dava açtığından şüpheliyim System.String.

orijinal

Her şeyden önce, WOW BCL çalışanları bazı performans optimizasyonlarıyla çok yaratıcı oldular. Birçok ait Stringyöntemlerle artık Konu statik önbelleğe kullanılarak yapılmaktadır StringBuildernesneyi.

Bir süre o yol izledi, ancak StringBuilderüzerinde kullanılmaz TrimBen bir Konu statik sorun olamazdı karar, kod yolu.

Sanırım aynı hatanın garip bir tezahürünü buldum.

Bu kod erişim ihlaliyle başarısız oluyor:

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);
    }
}

Yorumsuz size Ancak, //new A<int>(out s);içinde Maindaha sonra kod gayet güzel çalışıyor. Aslında, Aherhangi bir referans türüyle yeniden bağlanırsa, program başarısız olur, ancak Aherhangi bir değer türüyle yeniden bağlanırsa kod başarısız olmaz. Ayrıca A, statik yapıcı yorum yaparsanız , kod asla başarısız olmaz. İçine kazma sonra Trimve Formatproblem olduğu açıktır Lengthinlined olan ve yukarıda bu örneklerin bu Stringtip başlatılmamış. Özel olarak, gövdesi içinde Abireyin yapıcı, string.Emptydoğru olmasa da gövdesinin içinde, atanmamışsa Main, string.Emptydoğru bir şekilde tayin edilir.

Benim için şaşırtıcı bir Stringşekilde bir şekilde Atiplendirmenin bir değer tipiyle ilişkilendirilip birleştirilmemesine bağlı olması şaşırtıcı . Tek teorim, genel tip başlatma için tüm türler arasında paylaşılan bazı optimize edici JIT kod yolu olması ve bu yolun BCL referans türleri ("özel tipler?") Ve durumları hakkında varsayımlar yapmasıdır. Diğer BCL sınıfları olsa hızlı bir bakış public statictemelde alanları gösterir hepsi bunların statik yapıcısı (hatta olduğu gibi boş kurucular eder ve veri ile uygulamak System.DBNullve System.Emptybirlikte. BCL değer türleri public staticalanları statik bir yapıcısı (uygulamak görünmüyor System.IntPtr) örneğin Bu, JIT'in BCL referans türü başlatma hakkında bazı varsayımlar yaptığını göstermektedir.

FYI İşte iki versiyon için JITed kodu:

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 
    }

Kodun ( Main) geri kalanı iki sürüm arasında aynıdır.

DÜZENLE

Ek olarak, iki versiyondan gelen IL, ilk versiyonun IL'sini içeren A.ctorin çağrısı haricinde aynıdır B.Main():

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

karşı

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

saniyede.

Dikkat edilmesi gereken başka bir şey, JITed kodunun A<int>.ctor(out string): genel olmayan sürümdeki ile aynı olmasıdır.


3
Çok benzer bir yol boyunca cevapları araştırdım, ancak hiçbir yere götüremiyor gibi görünüyor. Bu bir dize sınıfı sorunu gibi görünüyor ve umarım daha genel bir sorun değildir. Şu anda kaynak kodlu birisinin (Eric) gelip neyin yanlış gittiğini ve başka bir şey olup olmadığını açıklamasını bekliyorum. Küçük bir fayda olarak bu tartışma zaten bir kullanması gerekip gerekmediğini tartışmaya yerleşmiş string.Emptyya ""... :)
Gleno

Aralarındaki IL aynı mı?
Cole Johnson

49
İyi analiz! Bunu BCL ekibine ileteceğim. Teşekkürler!
Eric Lippert

2
@EricLippert ve diğerleri: Bunun gibi kodun typeof(string).GetField("Empty").SetValue(null, "Hello world!"); Console.WriteLine(string.Empty);.NET 4.0 ve .NET 4.5'e karşı farklı sonuçlar verdiğini keşfettim . Bu değişiklik yukarıda açıklanan değişiklikle mi ilgili? .NET 4.5 bir alan değerini değiştirmemi teknik olarak nasıl görmezden gelebilir ? Belki bu konuda yeni bir soru sormalıyım?
Jeppe Stig Nielsen

4
@JeppeStigNielsen: Sorularınızın cevapları: "belki", "oldukça kolay, görünüşte" ve "bu bir soru-cevap sitesi, yani evet, sorunuza daha iyi bir cevap vermek istiyorsanız bu iyi bir fikir "belki"
Eric Lippert

3

Bunun .NET 4.0'daki (ile ilişkili BeforeFieldInit) bu optimizasyondan kaynaklandığından şüpheleniyorum .

Eğer doğru hatırlıyorsam:

Bir statik kurucuyu açıkça beforefieldinitbildirdiğinizde, çalışma zamanına herhangi bir statik üye erişiminden önce statik kurucunun çalıştırılması gerektiğini bildirir .

Tahminim:

Ben her nasılsa, x64 JITer bu gerçeği berbat olduğunu tahmin ediyorum böylece zaman bir farklı türünün statik üyesi olan bir sınıf erişilir kendi statik yapıcı zaten tükendi, bir şekilde atlar (yanlış sırada veya yürütür) çalıştıran statik yapıcı - ve bu nedenle çökmeye neden olur. (Boş gösterici istisnası alamıyorsunuz, bunun nedeni büyük olasılıkla sıfırla başlatılmamış olmasıdır.)

Ben var değil bu bölümü yanlış olabilir, böylece kod çalıştırmasına - ama başka tahmin yürütmem gerekirse, ben o şey olabilir derdim string.Format(veya Console.WriteLinedahili olarak en gibi çökmesine neden olduğu benzer,) erişmek için gereken belki de açık statik yapıya ihtiyaç duyan yerel ayarla ilgili bir sınıftır.

Yine, test etmedim, ama verilerdeki en iyi tahminim.

Hipotezimi test etmekten çekinmeyin ve nasıl gittiğini bana bildirin.


BStatik bir yapıcı olmadığında hata hala oluşur ve Abir değer türüyle yeniden ilişkilendirildiğinde oluşmaz . Bence biraz daha karmaşık.
Michael Graczyk

@MichaelGraczyk: Sanırım bunu açıklayabilirim (yine tahminlerle). Bstatik bir yapıcıya sahip olmak çok önemli değil. Yana Astatik ctor sahiptir başka ad alanında bazı yerele ilgili sınıf ile karşılaştırıldığında, sırayla yukarı çalışma zamanı messes hangi çalıştırıldığı. Böylece bu alan henüz başlatılmadı. Bununla birlikte, Abir değer türüyle örnekleme yaparsanız , çalışma zamanının örnekleme yoluyla ikinci geçişi olabilir A(CLR bunu zaten bir optimizasyon olarak bir referans türüyle önceden örneklendirmiştir), bu nedenle sipariş ikinci kez çalıştırıldığında çalışır .
user541686

@MichaelGraczyk: Bu tam bir açıklama olmasa bile - Sanırım verilen beforefieldinitoptimizasyonun temel nedeni olduğuna ikna oldum . Gerçek açıklamanın bir kısmı bahsettiğimden farklı olabilir, ancak temel neden muhtemelen aynı şeydir.
user541686

IL'ye daha çok baktım ve bence sen bir şeylerin üzerindesin. İkinci geçiş fikrinin burada alakalı olacağını düşünmüyorum, çünkü keyfi olarak birçok çağrı yaparsam kod hala başarısız oluyor A<object>.ctor().
Michael Graczyk

@MichaelGraczyk: Duymak güzel ve test için teşekkürler. Maalesef kendi dizüstü bilgisayarımda üretemiyorum. (2010 4.0 x64) Gerçekten dize biçimlendirmesiyle (ör. Yerel ayarla ilgili) ilgili olup olmadığını kontrol edebilir misiniz? Bu parçayı kaldırırsanız ne olur?
user541686

1

Bir gözlem, ancak DotPeek ayrıştırılmış dizeyi gösterir.

/// <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()
  {
  }
}

Kendi Emptyözniteliğim dışında kendi yolumu beyan edersem , artık MDA'yı alamıyorum:

class A<T>
{
    static readonly string Empty;

    static A() { }

    public A()
    {
        string.Format("{0}", Empty);
    }
}

Ve ile bu özelliğe? Bunu zaten kurduk "".
Henk Holterman

Bu "Performans açısından kritik ..." Özniteliği, özniteliğin süslediği yöntemleri değil, Öznitelik yapıcısının kendisini etkiler.
Michael Graczyk

Dahili. Kendi özdeş niteliğimi tanımladığımda hala MDA'ya neden olmaz. Ben beklemek değil - JITter bu belirli bir özellik arıyor ise benim bulamazsınız.
lesscode
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.