.NET 4.5 बीटा में इस FatalExecutionEngineError का कारण क्या है? [बन्द है]


150

नीचे नमूना कोड स्वाभाविक रूप से हुआ। अचानक मेरा कोड एक बहुत बुरा लग रहा FatalExecutionEngineErrorअपवाद है। मैंने अपराधी के नमूने को अलग करने और कम करने की कोशिश करते हुए एक अच्छा 30 मिनट बिताया। कंसोल ऐप के रूप में विज़ुअल स्टूडियो 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 पर इस त्रुटि का उत्पादन करना चाहिए:

FatalExecutionException स्क्रीनशॉट

क्या यह एक ज्ञात बग है, क्या कारण है और मैं इसे कम करने के लिए क्या कर सकता हूं? आसपास मेरा वर्तमान कार्य उपयोग नहीं करना है string.Empty, लेकिन क्या मैं गलत पेड़ को काट रहा हूं? उस कोड के बारे में कुछ भी बदलने से यह कार्य करता है जैसा कि आप अपेक्षा करते हैं - उदाहरण के लिए खाली स्थैतिक निर्माणकर्ता को हटाने A, या प्रकार को बदलने के objectलिए int

मैंने अपने लैपटॉप पर इस कोड की कोशिश की और यह शिकायत नहीं की। हालाँकि, मैंने अपना मुख्य ऐप आज़माया और यह लैपटॉप पर भी क्रैश हो गया। मैंने समस्या को कम करते समय कुछ दूर मंगवा लिया होगा, मैं देखूंगा कि क्या मैं समझ सकता हूं कि क्या था।

मेरा लैपटॉप ऊपर के कोड के साथ क्रैश हुआ, फ्रेमवर्क 4.0 के साथ, लेकिन मुख्य क्रैश 4.5 के साथ भी। दोनों सिस्टम नवीनतम अपडेट (जुलाई?) के साथ VS'12 का उपयोग कर रहे हैं।

अधिक जानकारी :

  • आईएल कोड (संकलित डिबग / कोई सीपीयू / 4.0 / वीएस2010 (आईडीई कोई फर्क नहीं पड़ता?)): Http://codepad.org/boZDd98E
  • वीएस 2010 को 4.0 के साथ नहीं देखा गया। अनुकूलन के साथ / बिना दुर्घटनाग्रस्त, अलग-अलग लक्ष्य सीपीयू, डिबगर संलग्न / संलग्न नहीं, आदि। टिम मेडोरा
  • 2010 में क्रैश अगर मैं AnyCPU का उपयोग करता हूं, तो x86 में ठीक है। दृश्य स्टूडियो 2010 SP1 में क्रैश, प्लेटफ़ॉर्म लक्ष्य = AnyCPU का उपयोग करते हुए, लेकिन प्लेटफ़ॉर्म लक्ष्य = x86 के साथ ठीक है। इस मशीन में VS2012RC लगा है और साथ ही 4.5 संभवत: इन-प्लेस रिप्लेसमेंट कर रहा है। AnyCPU और TargetPlatform = 3.5 का उपयोग करें तो यह क्रैश नहीं होता है इसलिए फ्रेमवर्क में रिग्रेशन जैसा दिखता है ।- colinsmith
  • 4.0 के साथ VS2010 में x86, x64 या AnyCPU पर पुन: पेश नहीं कर सकता। - फूजी
  • केवल x64 के लिए होता है, (2012rc, Fx4.5) - हेंक
  • Win8 RP पर VS2012 आरसी। .NET 4.5 को टारगेट करते समय शुरू में इस MDA को नहीं देखना। जब .NET 4.0 को लक्ष्य करने के लिए स्विच किया गया तो MDA दिखाई दिया। फिर वापस .NET 4.5 पर स्विच करने के बाद MDA रहता है। - वेन

मैं कभी नहीं जानता था कि आप एक सार्वजनिक के साथ-साथ एक स्थिर निर्माण भी कर सकते हैं। हेक मुझे कभी नहीं पता था कि स्थिर निर्माणकर्ता मौजूद थे।
कोल जॉनसन

मेरे पास एक विचार है: क्योंकि आप B को एक स्थिर वर्ग से कुछ हद तक स्थिर वर्ग होने के कारण बदल रहे हैं?
कोल जॉनसन

@ क्रिसस्क्लेयर, मुझे ऐसा नहीं लगता। मेरा मतलब है कि मैंने अपने लैपटॉप पर इस कोड का परीक्षण किया, और वही परिणाम मिले।
ग्लेनो

@ColeJohnson हाँ आईएल सभी लेकिन एक स्पष्ट जगह में मेल खाता है। सी # संकलक के यहाँ कोई बग दिखाई नहीं देता है।
माइकल ग्रेसीक

14
यहां मूल रिपोर्टिंग के लिए दोनों को धन्यवाद, और माइकल को उनके उत्कृष्ट विश्लेषण के लिए। सीएलआर पर मेरे समकक्षों ने यहां बग को पुन: पेश करने की कोशिश की और पता चला कि यह 64 बिट सीएलआर के "रिलीज कैंडिडेट" संस्करण पर पुन: पेश करता है, लेकिन अंतिम "विनिर्माण के लिए जारी" संस्करण पर नहीं, जिसमें बग फिक्स पोस्ट की एक संख्या थी। आर सी। (RTM संस्करण अगस्त 15 तारीख को जनता के लिए उपलब्ध हो जाएगा, 2012) वे इसलिए मानते हैं कि यह एक है कि यहां की सूचना मिली थी के रूप में ही मुद्दा: connect.microsoft.com/VisualStudio/feedback/details/737108/...
एरिक लिपर्ट

जवाबों:


114

यह भी एक पूर्ण उत्तर नहीं है, लेकिन मेरे पास कुछ विचार हैं।

मेरा मानना ​​है कि मैंने .NET जेआईटी टीम से किसी के बिना उत्तर के रूप में अच्छा स्पष्टीकरण पाया है।

अपडेट करें

मैं थोड़ा गहरा लग रहा था, और मुझे विश्वास है कि मुझे इस मुद्दे का स्रोत मिल गया है। यह JIT टाइप-इनिशियलाइज़ेशन लॉजिक में बग के संयोजन के कारण प्रतीत होता है, और C # कंपाइलर में एक बदलाव जो इस धारणा पर निर्भर करता है कि JIT इरादा के अनुसार काम करता है। मुझे लगता है कि JIT बग .NET 4.0 में मौजूद था, लेकिन .NET 4.5 के कंपाइलर में बदलाव से इसे उजागर किया गया था।

मुझे नहीं लगता कि beforefieldinitयहां केवल यही मुद्दा है। मुझे लगता है कि यह उससे ज्यादा सरल है।

System.String.NET 4.0 से mscorlib.dll के प्रकार में एक स्थिर निर्माता है:

.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 के बीच दो चीजें बदल गई हैं:

पहला: ईई को बदल दिया गया था ताकि यह स्वचालित रूप से प्रारंभ हो जाए String.Empty से अनवांटेड कोड से हो जाए। यह परिवर्तन संभवतः .NET 4.0 के लिए किया गया था।

दूसरा: संकलक बदल गया ताकि यह स्ट्रिंग के लिए एक स्थिर निर्माता का उत्सर्जन न करे, यह जानकर String.Empty इसे अप्रबंधित पक्ष से सौंपा जाएगा। यह परिवर्तन .NET 4.5 के लिए किया गया प्रतीत होता है।

ऐसा प्रतीत होता है कि ईई जल्द ही कुछ अनुकूलन पथों के साथ पर्याप्त रूप से असाइन नहीं होता है String.Empty। संकलक के लिए किए गए परिवर्तन (या जो कुछ भी String.cctorगायब करने के लिए बदल गया है) को उम्मीद थी कि ईई इस असाइनमेंट को किसी भी उपयोगकर्ता कोड को निष्पादित करने से पहले कर देगा, लेकिन ऐसा प्रतीत होता है कि ईई इस असाइनमेंट String.Emptyको संदर्भ प्रकार के पुनरीक्षित सामान्य वर्गों के तरीकों में उपयोग करने से पहले नहीं करता है ।

अंत में, मेरा मानना ​​है कि बग JIT टाइप-इनिशियलाइज़ेशन लॉजिक में एक गहरी समस्या का संकेत है। ऐसा प्रतीत होता है कि कंपाइलर में बदलाव एक विशेष मामला है System.String, लेकिन मुझे संदेह है कि जेआईटी ने यहां एक विशेष मामला बनाया है System.String

मूल

सबसे पहले, वाह 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मूल्य मान के साथ पुन: परिभाषित किया जाता है या नहीं । मेरा एकमात्र सिद्धांत यह है कि जेनेरिक टाइप-इनिशियलाइज़ेशन के लिए कुछ अनुकूलन जेआईटी कोड पथ है जो सभी प्रकारों के बीच साझा किया जाता है, और वह रास्ता बीसीएल संदर्भ प्रकारों ("विशेष प्रकार?") और उनके राज्य के बारे में धारणा बनाता है। public staticखेतों के साथ अन्य बीसीएल वर्गों का एक त्वरित रूप से पता चलता है कि मूल रूप से उनमें से सभी एक स्थिर निर्माता को लागू करते हैं (यहां तक ​​कि खाली निर्माणकर्ताओं और कोई डेटा नहीं है, जैसे System.DBNullऔर फ़ील्ड के System.Emptyसाथ बीसीएल मूल्य प्रकार public staticएक स्थिर निर्माता को लागू करने के लिए नहीं लगते हैं ()System.IntPtr उदाहरण के लिए) । यह इंगित करता है कि जेसीएल बीसीएल संदर्भ प्रकार आरंभीकरण के बारे में कुछ धारणाएं बनाता है।

FYI यहाँ दो संस्करणों के लिए JITed कोड है:

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>...

क्षण में।

एक और ध्यान देने वाली बात यह है कि इसके लिए JITed कोड A<int>.ctor(out string): गैर-जेनेरिक संस्करण के समान है।


3
मैं एक बहुत ही समान रास्ते के साथ जवाब के लिए खोज की है, लेकिन यह कहीं भी नेतृत्व करने के लिए प्रतीत नहीं होता है। यह एक स्ट्रिंग क्लास समस्या प्रतीत होती है और उम्मीद है कि यह अधिक सामान्य समस्या नहीं है। इसलिए अभी मैं किसी (एरिक) के लिए स्रोत कोड के साथ आने और समझाने की प्रतीक्षा कर रहा हूं कि क्या गलत हुआ, और अगर कुछ और प्रभावित होता है। एक छोटे से लाभ के रूप में इस चर्चा ने पहले ही इस बहस को सुलझा दिया कि क्या किसी को इस्तेमाल करना चाहिए string.Emptyया ""... :)
Gleno

क्या उनके बीच IL समान है?
कोल जॉनसन

49
अच्छा विश्लेषण! मैं इसे बीसीएल टीम के साथ पारित कर दूंगा। धन्यवाद!
एरिक लिपर्ट

2
@EricLippert और अन्य: मुझे पता चला है कि जैसे कोड typeof(string).GetField("Empty").SetValue(null, "Hello world!"); Console.WriteLine(string.Empty);.NET 4.0 बनाम .NET 4.5 पर अलग-अलग परिणाम देता है। क्या यह परिवर्तन ऊपर वर्णित परिवर्तन से संबंधित है? .NET 4.5 तकनीकी रूप से मुझे फील्ड वैल्यू बदलने की अनदेखी कैसे कर सकता है शायद मुझे इस बारे में एक नया सवाल पूछना चाहिए?
जेपी स्टिग नीलसन

4
@JeppeStigNielsen: आपके सवालों के जवाब हैं: "शायद,", "काफी आसानी से, जाहिर है," और "यह एक सवाल-जवाब की साइट है, इसलिए हां, यह एक अच्छा विचार है यदि आप अपने प्रश्न का उत्तर बेहतर चाहते हैं।" 'शायद' से।
एरिक लिपर्ट

3

मैं .NET 4.0 में इस ऑप्टिमाइज़ेशन (संबंधित BeforeFieldInit) के कारण होता हूं ।

अगर मुझे ठीक से याद है:

जब आप किसी स्थिर कंस्ट्रक्टर को स्पष्ट रूप से घोषित करते हैं, beforefieldinitतो उत्सर्जित किया जाता है, रनटाइम को बताते हुए कि किसी भी स्थिर सदस्य तक पहुँचने से पहले स्टैटिक कंस्ट्रक्टर को चलाया जाना चाहिए

मेरा अनुमान:

मुझे लगता है कि वे किसी तरह इस तथ्य को x64 JITer पर खराब कर देते हैं, ताकि जब एक अलग प्रकार के स्थिर सदस्य को उस वर्ग से एक्सेस किया जाए जिसका स्वयं का स्थैतिक निर्माता पहले से ही चला हुआ है, तो यह किसी तरह से चल रहा है (या गलत क्रम में निष्पादित) स्थिर कंस्ट्रक्टर - और इसलिए दुर्घटना का कारण बनता है। (आपको एक शून्य सूचक अपवाद नहीं मिलता है, शायद इसलिए कि यह शून्य-प्रारंभिक नहीं है।)

मैंने आपका कोड नहीं चलाया है, इसलिए यह हिस्सा गलत हो सकता है - लेकिन अगर मुझे दूसरा अनुमान लगाना था, तो मैं कहूंगा कि यह कुछ string.Format(या Console.WriteLine, जो समान है) आंतरिक रूप से उपयोग करने की आवश्यकता है जिससे दुर्घटना हो सकती है, जैसे कि शायद एक स्थानीय- संबंधित वर्ग जिसे स्पष्ट स्थैतिक निर्माण की आवश्यकता है।

दोबारा, मैंने इसका परीक्षण नहीं किया है, लेकिन यह डेटा पर मेरा सबसे अच्छा अनुमान है।

मेरी परिकल्पना का परीक्षण करने के लिए स्वतंत्र महसूस करें और मुझे बताएं कि यह कैसे होता है।


बग अभी भी तब होता है जब Bएक स्थिर कंस्ट्रक्टर नहीं होता है , और यह तब नहीं होता है जब Aमूल्य प्रकार के साथ पुन: संशोधित किया जाता है। मुझे लगता है कि यह थोड़ा अधिक जटिल है।
माइकल ग्रेसीक

@MichaelGraczyk: मुझे लगता है कि मैं (फिर से, अनुमान के साथ) समझा सकता हूं। Bस्टैटिक कंस्ट्रक्टर होने से ज्यादा फर्क नहीं पड़ता। चूंकि Aएक स्थिर ctor है, रनटाइम उस क्रम को गड़बड़ कर देता है जिसमें इसे कुछ अन्य नामस्थानों में कुछ लोकेल से संबंधित वर्ग के साथ तुलना करने पर चलाया जाता है। इसलिए उस क्षेत्र को अभी शुरू नहीं किया गया है। हालाँकि, यदि आप Aएक मूल्य प्रकार के साथ तात्कालिकता करते हैं, तो यह इंस्टेंटिअटिंग के माध्यम से रनटाइम का दूसरा पास हो सकता है A(सीएलआर ने पहले से ही इसे एक संदर्भ के रूप में पूर्व-तात्कालिक रूप से अनुकूलन के रूप में देखा है) इसलिए यह दूसरी बार चलने पर आदेश काम करता है ।
user541686

@MichaelGraczyk: भले ही यह काफी स्पष्टीकरण नहीं है, हालांकि - मुझे लगता है कि मैं काफी आश्वस्त हूं कि दिए गए beforefieldinitअनुकूलन मूल कारण हैं। यह हो सकता है कि कुछ वास्तविक स्पष्टीकरण मेरे द्वारा बताए गए से अलग हैं, लेकिन मूल कारण संभवतः एक ही बात है।
user541686

मैंने आईएल में अधिक देखा, और मुझे लगता है कि आप कुछ पर हैं। मुझे नहीं लगता कि दूसरा पास विचार यहां प्रासंगिक होने जा रहा है, क्योंकि अगर मैं मनमाने ढंग से कई कॉल करता हूं तो कोड अभी भी विफल है A<object>.ctor()
माइकल ग्रेस्कक

@MichaelGraczyk: सुनने के लिए अच्छा है, और उस परीक्षण के लिए धन्यवाद। मैं दुर्भाग्य से अपने लैपटॉप पर इसे पुन: पेश नहीं कर सकता। (2010 4.0 x 64) क्या आप यह देखने के लिए जांच सकते हैं कि क्या यह वास्तव में स्ट्रिंग-फॉर्मेटिंग (यानी स्थानीय-संबंधित) से संबंधित है? अगर आप उस हिस्से को हटा दें तो क्या होगा?
user541686

1

एक अवलोकन, लेकिन DotPeek विघटित स्ट्रिंग दिखाता है। इस प्रकार खाली करें:

/// <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बिना किसी विशेषता के सिवाय अपना तरीका घोषित करता हूं , तो मुझे एमडीए नहीं मिलता है:

class A<T>
{
    static readonly string Empty;

    static A() { }

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

और उस विशेषता के साथ ? हमने ""इसे पहले ही स्थापित कर दिया है।
हेन्क होल्टरमैन

यह "प्रदर्शन क्रिटिकल ..." विशेषता एट्रिब्यूट कंस्ट्रक्टर को ही प्रभावित करती है, न कि उन तरीकों को जो कि विशेषता को दर्शाते हैं।
माइकल ग्रेस्कक

यह आंतरिक है। जब मैं अपने स्वयं के समान विशेषता को परिभाषित करता हूं तो यह अभी भी एमडीए का कारण नहीं बनता है। ऐसा नहीं है कि मैं इसकी उम्मीद करूंगा - अगर जेटर उस विशिष्ट विशेषता की तलाश कर रहा है तो उसे मेरा नहीं मिलेगा।
कम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.