Domanda

Riepilogo:

Periodicamente ricevo un errore irreversibile del motore di esecuzione .NET su un'applicazione di cui non riesco a eseguire il debug.La finestra di dialogo che appare offre solo la chiusura del programma o l'invio di informazioni sull'errore a Microsoft.Ho provato a guardare le informazioni più dettagliate ma non so come utilizzarle.

Errore:

L'errore è visibile nel Visualizzatore eventi in Applicazioni ed è il seguente:

.NET Runtime Versione 2.0.50727.3607 - Errore del motore di esecuzione fatale (7A09795E) (80131506)

Il computer che lo esegue è Windows XP Professional SP 3.(Intel Core2Quad Q6600 2,4 GHz con 2,0 GB di RAM) Altri progetti basati su .NET privi di download multi-thread (vedi sotto) sembrano funzionare bene.

Applicazione:

L'applicazione è scritta in C#/.NET 3.5 utilizzando VS2008 e installata tramite un progetto di installazione.

L'app è multi-thread e scarica i dati da più server Web utilizzando System.Net.HttpWebRequest e i suoi metodi.Ho stabilito che l'errore .NET ha qualcosa a che fare con il threading o con HttpWebRequest, ma non sono riuscito ad avvicinarmi ulteriormente poiché questo particolare errore sembra impossibile da eseguire il debug.

Ho provato a gestire gli errori su molti livelli, inclusi i seguenti in Program.cs:

// handle UI thread exceptions
Application.ThreadException += Application_ThreadException;

// handle non-UI thread exceptions
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

// force all windows forms errors to go through our handler
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

Altre note e cosa ho provato...

  • Ho installato Visual Studio 2008 sul computer di destinazione e ho provato a eseguirlo in modalità debug, ma l'errore si verifica ancora, senza alcun suggerimento su dove si sia verificato nel codice sorgente.
  • Quando si esegue il programma dalla versione installata (Release), l'errore si verifica più frequentemente, in genere entro pochi minuti dall'avvio dell'applicazione.Quando si esegue il programma in modalità debug all'interno di VS2008, può funzionare per ore o giorni prima di generare l'errore.
  • Ho reinstallato .NET 3.5 e mi sono assicurato che tutti gli aggiornamenti fossero applicati.
  • Per la frustrazione, ha rotto oggetti casuali nel cubicolo.
  • Parti di codice riscritte che si occupano del threading e del download nel tentativo di catturare e registrare le eccezioni, sebbene la registrazione sembrasse aggravare il problema (e non ha mai fornito alcun dato).

Domanda:

Quali passaggi posso eseguire per risolvere o eseguire il debug di questo tipo di errore?I dump della memoria e simili sembrano essere il passo successivo, ma non ho esperienza nell'interpretarli.Forse c'è qualcos'altro che posso fare nel codice per cercare di individuare gli errori...Sarebbe bello se l'"Errore irreversibile del motore di esecuzione" fosse più informativo, ma le ricerche su Internet mi hanno solo detto che si tratta di un errore comune per molti elementi relativi a .NET.

È stato utile?

Soluzione

Bene, hai un grosso problema. Tale eccezione è sollevata dal CLR quando rileva che il garbage collection integrità mucchio è compromessa. danneggiamento di heap, la rovina di ogni programmatore che abbia mai scritto il codice in un linguaggio non gestito, come C o C ++.

Queste lingue rendono molto facile da danneggiare l'heap, tutto quello che serve è quello di scrivere oltre la fine di un array che è allocato sul mucchio. O utilizzando la memoria dopo che è stato rilasciato. O che hanno un valore male per un puntatore. Il tipo di Bugz codice gestito che è stato inventato da risolvere.

Ma si utilizza codice gestito, a giudicare dalla tua domanda. Beh, per lo più, tuo il codice è gestito. Ma si sta eseguendo un sacco di codice non gestito. Tutto il codice di basso livello che fa in realtà un lavoro HttpWebRequest non è gestito. E così è il CLR, è stato scritto in C ++ in modo tecnicamente la stessa probabilità di danneggiare l'heap. Ma dopo più di quattromila revisioni di esso, e milioni di programmi di utilizzo, le probabilità che soffre ancora di pidocchi heap sono molto piccolo.

Lo stesso non è vero per tutti gli altri codice non gestito che vuole un pezzo di HttpWebRequest. Il codice che non si conoscono perché non hai scritto e non è documentato da Microsoft. Il firewall. Il vostro programma antivirus. monitor di utilizzo di Internet della vostra azienda. Signore sa la cui "acceleratore di download".

isolare il problema, assumere non è né il codice né il codice di Microsoft che causa il problema. Si supponga che è il primo ambientale e sbarazzarsi del crapware.

Per una storia epica FEEE ambientale, leggere questa discussione .

Altri suggerimenti

Poiché i suggerimenti precedenti sono di natura abbastanza generica, ho pensato che potesse essere utile pubblicare la mia battaglia contro questa eccezione con esempi di codice specifici, le modifiche in background che ho implementato per causare questa eccezione e come l'ho risolta.

Innanzitutto, la versione breve:Stavo utilizzando una DLL interna scritta in C++ (non gestita).Ho passato un array di una dimensione specifica dal mio eseguibile .NET.Il codice non gestito ha tentato di scrivere in una posizione dell'array non allocata dal codice gestito.Ciò ha causato un danneggiamento della memoria che è stato successivamente impostato per la raccolta dati inutili.Quando il Garbage Collector si prepara a raccogliere memoria, controlla innanzitutto lo stato della memoria (e i limiti).Quando trova la corruzione, BOOM.

Ora la versione TL;DR:

Sto utilizzando una DLL non gestita sviluppata internamente, scritta in C++.Il mio sviluppo della GUI è in C# .Net 4.0.Sto chiamando una varietà di questi metodi non gestiti.Quella DLL funge effettivamente da fonte di dati.Un esempio di definizione esterna dalla dll:

    [DllImport(@"C:\Program Files\MyCompany\dataSource.dll",
        EntryPoint = "get_sel_list",
        CallingConvention = CallingConvention.Winapi)]
    private static extern int ExternGetSelectionList(
        uint parameterNumber,
        uint[] list,
        uint[] limits,
        ref int size);

Quindi avvolgo i metodi nella mia interfaccia per utilizzarli durante il mio progetto:

    /// <summary>
    /// Get the data for a ComboBox (Drop down selection).
    /// </summary>
    /// <param name="parameterNumber"> The parameter number</param>
    /// <param name="messageList"> Message number </param>
    /// <param name="valueLimits"> The limits </param>
    /// <param name="size"> The maximum size of the memory buffer to 
    /// allocate for the data </param>
    /// <returns> 0 - If successful, something else otherwise. </returns>
    public int GetSelectionList(uint parameterNumber, 
           ref uint[] messageList, 
           ref uint[] valueLimits, 
           int size)
    {
        int returnValue = -1;
        returnValue = ExternGetSelectionList(parameterNumber,
                                         messageList, 
                                         valueLimits, 
                                         ref size);
        return returnValue;
    }

Una chiamata di esempio di questo metodo:

            uint[] messageList = new uint[3];
            uint[] valueLimits = new uint[3];
            int dataReferenceParameter = 1;

            // BUFFERSIZE = 255.
            MainNavigationWindow.MainNavigationProperty.DataSourceWrapper.GetSelectionList(
                          dataReferenceParameter, 
                          ref messageList, 
                          ref valueLimits, 
                          BUFFERSIZE);

Nella GUI, si naviga attraverso diverse pagine contenenti una varietà di grafici e input dell'utente.Il metodo precedente mi ha permesso di popolare i dati ComboBoxes.Un esempio della mia configurazione di navigazione e chiamata al momento prima di questa eccezione:

Nella mia finestra host, ho impostato una proprietà:

    /// <summary>
    /// Gets or sets the User interface page
    /// </summary>
    internal UserInterfacePage UserInterfacePageProperty
    {
        get
        {
            if (this.userInterfacePage == null)
            {
                this.userInterfacePage = new UserInterfacePage();
            }

            return this.userInterfacePage;
        }

        set { this.userInterfacePage = value; }
    }

Quindi, quando necessario, vado alla pagina:

MainNavigationWindow.MainNavigationProperty.Navigate(
        MainNavigation.MainNavigationProperty.UserInterfacePageProperty);

Tutto ha funzionato abbastanza bene, anche se ho avuto alcuni seri problemi striscianti.Durante la navigazione utilizzando l'oggetto (Metodo NavigationService.Navigate (oggetto)), l'impostazione predefinita per IsKeepAlive la proprietà è true.Ma la questione è più nefasta di così.Anche se imposti il IsKeepAlive valore nel costruttore di quella pagina specificatamente a false, viene ancora lasciato solo dal garbage collector come se lo fosse true.Ora, per molte delle mie pagine, questo non è stato un grosso problema.Avevano piccole impronte di memoria senza che succedesse molto.Ma molte altre di queste pagine contenevano grandi grafici altamente dettagliati a scopo illustrativo.Non passò molto tempo prima che il normale utilizzo di questa interfaccia da parte degli operatori delle nostre apparecchiature causasse enormi allocazioni di memoria che non venivano mai cancellate e alla fine bloccavano tutti i processi sulla macchina.Dopo che l'impeto dello sviluppo iniziale si è trasformato da uno tsunami in una vera e propria noia, ho finalmente deciso di affrontare le perdite di memoria una volta per tutte.Non entrerò nei dettagli di tutti i trucchi che ho implementato per ripulire la memoria (Riferimento deboles alle immagini, sganciando i gestori di eventi su Unload(), utilizzando un timer personalizzato che implementa il IWeakEventListener interfaccia, ecc...).La modifica chiave che ho apportato è stata quella di navigare tra le pagine utilizzando l'URI anziché l'oggetto (Metodo NavigationService.Navigate (Uri)).Esistono due differenze importanti quando si utilizza questo tipo di navigazione:

  1. IsKeepAlive è impostato per false per impostazione predefinita.
  2. Il Garbage Collector ora proverà a ripulire l'oggetto di navigazione come se IsKeepAlive era impostato su false.

Quindi ora la mia navigazione è simile a:

MainNavigation.MainNavigationProperty.Navigate(
    new Uri("/Pages/UserInterfacePage.xaml", UriKind.Relative));

Qualcos'altro da notare qui:Ciò non influisce solo sul modo in cui gli oggetti vengono ripuliti dal Garbage Collector, ma influisce anche sul modo in cui sono inizialmente allocato in memoria, come avrei presto scoperto.

Tutto sembrava funzionare alla grande.La mia memoria veniva rapidamente ripulita fino a raggiungere il mio stato iniziale mentre navigavo tra le pagine ad alta intensità grafica, finché non raggiungevo questa particolare pagina con quella particolare chiamata alla dll dataSource per riempire alcuni comboBox.Poi ho avuto questa brutta cosa FatalEngineExecutionError.Dopo giorni di ricerca e di ricerca di vaghi suggerimenti o soluzioni altamente specifiche che non si applicavano a me, oltre a aver utilizzato quasi tutte le armi di debug nel mio arsenale di programmazione personale, ho finalmente deciso che l'unico modo in cui avrei davvero inchiodato questo down era la misura estrema di ricostruire una copia esatta di questa particolare pagina, elemento per elemento, metodo per metodo, riga per riga, finché non mi sono finalmente imbattuto nel codice che ha generato questa eccezione.È stato noioso e doloroso come sto lasciando intendere, ma alla fine l'ho rintracciato.

Si è scoperto che era nel modo in cui la DLL non gestita stava allocando memoria per scrivere i dati negli array che stavo inviando per il popolamento.Quel particolare metodo esaminerebbe effettivamente il numero del parametro e, da tali informazioni, allocherebbe un array di una dimensione particolare in base alla quantità di dati che prevedeva di scrivere nell'array che ho inviato.Il codice che si è bloccato:

            uint[] messageList = new uint[2];
            uint[] valueLimits = new uint[2];
            int dataReferenceParameter = 1;

            // BUFFERSIZE = 255.
            MainNavigationWindow.MainNavigationProperty.DataSourceWrapper.GetSelectionList(
                           dataReferenceParameter, 
                           ref messageList, 
                           ref valueLimits, 
                           BUFFERSIZE);

Questo codice potrebbe sembrare identico all'esempio precedente, ma presenta una piccola differenza.La dimensione dell'array che assegno è 2 non 3.L'ho fatto perché sapevo che questo particolare ComboBox avrebbe avuto solo due elementi di selezione rispetto agli altri ComboBox sulla pagina che avevano tutti tre elementi di selezione.Tuttavia il codice non gestito non vedeva le cose nel modo in cui le vedevo io.Ha ottenuto l'array che avevo consegnato e ha provato a scrivere un array size[ 3 ] nella mia allocazione size[ 2 ], e basta.* scoppio! * * incidente! * Ho modificato la dimensione dell'allocazione su 3 e l'errore è scomparso.

Ora questo particolare codice era già in esecuzione senza questo errore da almeno un anno.Ma il semplice atto di navigare in questa pagina tramite a Uri al contrario di un Object ha fatto apparire lo schianto.Ciò implica che l'oggetto iniziale deve essere allocato in modo diverso a causa del metodo di navigazione che ho utilizzato.Dal momento che con il mio vecchio metodo di navigazione, la memoria veniva semplicemente accumulata al suo posto e lasciata a che fare come ritenevo opportuno per l'eternità, non sembrava importare se fosse un po' danneggiata in una o due piccole posizioni.Una volta che il Garbage Collector ha dovuto effettivamente fare qualcosa con quella memoria (come ripulirla), ha rilevato il danneggiamento della memoria e ha lanciato l'eccezione.Ironicamente, la mia principale perdita di memoria stava nascondendo un errore fatale di memoria!

Ovviamente esamineremo questa interfaccia per evitare che tali semplici ipotesi causino tali arresti anomali in futuro.Spero che questo aiuti altri a scoprire cosa sta succedendo nel loro codice.

Una presentazione che potrebbe essere un bel tutorial su dove cominciare con questo tipo di problema è questo: la produzione Hardcore debug in .NET da Ingo Rammer .

Faccio un po 'di un C ++ / CLI di codifica, e la corruzione heap di solito non provocare questo errore; di solito danneggiamento di heap o provoca un danneggiamento dei dati e una successiva un'eccezione normale o un errore di protezione della memoria -. che probabilmente non significa nulla

Oltre a cercare .net 4.0 (che carica il codice non gestito in modo diverso) si dovrebbe confrontare x86 e x64 edizioni del CLR - se possibile - la versione x64 ha un grande spazio di indirizzi e quindi completamente diverso malloc (+ frammentazione) e il comportamento quindi basta essere fortunato e di avere un errore ci diverso (più debuggable) (se si verifica affatto).

Inoltre, avete acceso il debug di codice non gestito nel debugger (un'opzione di progetto), quando si esegue con lo studio visivo? E hai gestito Assistenti di debug on?

Nel mio caso avevo installato un gestore di eccezioni con AppDomain.CurrentDomain.FirstChanceException. Questo gestore è stato registrando alcune eccezioni, e tutto andava bene per alcuni anni (in realtà questo codice di debug non dovuto restare in produzione).

Ma a seguito di un errore di configurazione, il logger ha cominciato a fallire, e il gestore stesso è stato Lanciare, che a quanto pare ha comportato una FatalExecutionEngineError apparentemente proveniente dal nulla.

Così chiunque incontrare questo errore potrebbe trascorrere qualche secondo la ricerca di occorrenze di FirstChanceException in qualsiasi parte del codice e forse salvare un paio di ore di grattarsi la testa:)

Se si utilizza Thread.sleep () che può essere il motivo. codice non gestito può essere sleeped solo dal sonno kernell.32 () la funzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top