Устранение неполадок .NET "Ошибка механизма фатального выполнения" - PullRequest
42 голосов
/ 13 мая 2010

Сводка:

Я периодически получаю сообщение об ошибке .NET Fatal Execution Engine в приложении, которое мне не удается отладить.В открывшемся диалоговом окне предлагается только закрыть программу или отправить информацию об ошибке в Microsoft.Я пытался просмотреть более подробную информацию, но не знаю, как ее использовать.

Ошибка:

Ошибка видна в средстве просмотра событийв разделе Приложения и выглядит следующим образом:

.NET Runtime версия 2.0.50727.3607 - Ошибка механизма фатального выполнения (7A09795E) (80131506)

Компьютер, на котором работает Windows XPProfessional SP 3. (Intel Core2Quad Q6600 2,4 ГГц с 2,0 ГБ ОЗУ) Другие проекты на базе .NET, в которых отсутствует многопоточная загрузка (см. Ниже), работают нормально.

Приложение:

Приложение написано на C # /. NET 3.5 с использованием VS2008 и установлено через проект установки.

Приложение является многопоточным и загружает данные с нескольких веб-серверов с использованием System.Net.HttpWebRequest и его методы.Я определил, что ошибка .NET связана с многопоточностью или HttpWebRequest, но я не смог подобраться ближе, так как эту конкретную ошибку невозможно исправить.

Я пытался обрабатывать ошибкина многих уровнях, включая следующие в 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);

Дополнительные заметки и то, что я пробовал ...

  • Установленная Visual Studio2008 на целевой машине и попытался запустить в режиме отладки, но ошибка все еще возникает, без подсказки относительно того, где в исходном коде это произошло.
  • При запуске программы из установленной версии (Release) возникает ошибкачаще, как правило, в течение нескольких минут после запуска приложения.Когда программа запускается в режиме отладки внутри VS2008, она может работать часами или днями, прежде чем возникнет ошибка.
  • Переустановил .NET 3.5 и убедился, что все обновления применены.
  • Сломал случайную ячейкуразочарованные объекты.
  • Переписаны части кода, связанные с многопоточностью и загрузкой, при попытках перехвата и регистрации исключений, хотя ведение журнала, казалось, усугубляло проблему (и никогда не предоставляло никаких данных).

Вопрос:

Какие шаги я могу предпринять для устранения неполадок или устранения ошибок такого рода?Дампы памяти и тому подобное, кажется, следующий шаг, но у меня нет опыта в их интерпретации.Возможно, в коде есть что-то большее, что я могу сделать, чтобы попытаться отловить ошибки ... Было бы неплохо, если бы «Ошибка механизма фатального выполнения» была более информативной, но поиски в Интернете показали мне, что это распространенная ошибка для многихЭлементы, связанные с .NET.

Ответы [ 5 ]

43 голосов
/ 21 июня 2010

Ну, у вас есть большая проблема. Это исключение вызывается CLR, когда он обнаруживает, что целостность кучи, собираемой мусором, нарушена. Повреждение кучи, проклятие любого программиста, который когда-либо писал код на неуправляемом языке, таком как C или C ++.

Эти языки делают очень легким для повреждения кучи, все, что нужно, это записать за конец массива, который расположен в куче. Или используя память после ее освобождения. Или плохое значение для указателя. Багз, который управлял кодом, был изобретен для решения.

Но вы используете управляемый код, судя по вашему вопросу. Ну, в основном, ваш код управляется. Но вы выполняете лотов неуправляемого кода. Весь низкоуровневый код, который фактически делает работу HttpWebRequest, неуправляем. Как и CLR, он был написан на C ++, так что технически с такой же вероятностью может испортить кучу. Но после более четырех тысяч его пересмотров и миллионов программ, использующих его, шансы, что он все еще страдает от кучи, очень малы: * * * * * * * * То же самое не относится ко всему другому неуправляемому коду, который хочет кусок HttpWebRequest. Код, о котором вы не знаете, потому что вы его не написали и не задокументированы Microsoft. Ваш брандмауэр. Ваш вирусный сканер. Монитор использования Интернет вашей компании. Господь знает, чей «ускоритель загрузки».

Изолируйте проблему, предположите, что это не ваш код или код Microsoft, который вызывает проблему. Предположим, что это в первую очередь относится к окружающей среде, и избавьтесь от посуды.

Прочитать эту тему .

для эпической истории о FEEE.
6 голосов
/ 27 августа 2014

Поскольку предыдущие предложения носят довольно общий характер, я подумал, что было бы полезно опубликовать свое собственное сражение с этим исключением с конкретными примерами кода, изменениями фона, которые я реализовал, чтобы вызвать это исключение, и тем, как я его решил .

Во-первых, короткая версия: я использовал собственную dll, написанную на C ++ (неуправляемую). Я передал массив определенного размера из моего исполняемого файла .NET. Неуправляемый код пытался записать в расположение массива, которое не было выделено управляемым кодом. Это вызвало повреждение памяти, которое позже было установлено как сборщик мусора. Когда сборщик мусора готовится собирать память, он сначала проверяет состояние памяти (и границы). Когда он находит коррупцию, BOOM .

Ниже приведена версия TL; DR:

Я использую неуправляемую DLL, разработанную собственными силами, написанную на C ++. Моя собственная разработка GUI находится на C # .Net 4.0. Я называю множество этих неуправляемых методов. Это DLL эффективно выступает в качестве моего источника данных. Пример внешнего определения из 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);

Затем я обертываю методы в своем собственном интерфейсе для использования в моем проекте:

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

Пример вызова этого метода:

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

В графическом интерфейсе можно перемещаться по разным страницам, содержащим различные графические элементы и пользовательский ввод. Предыдущий метод позволил мне получить данные для заполнения ComboBoxes. Пример моей настройки навигации и вызова в момент до этого исключения:

В моем окне хоста я установил свойство:

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

Затем, при необходимости, я перехожу на страницу:

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

Все работало достаточно хорошо, хотя у меня были некоторые серьезные проблемы с ползучестью. При навигации с использованием объекта ( Метод NavigationService.Navigate (Object) ) по умолчанию для свойства IsKeepAlive установлено true. Но проблема более гнусна, чем эта. Даже если вы установите значение IsKeepAlive в конструкторе этой страницы специально для false, оно все равно останется сборщиком мусора, как если бы оно было true. Теперь для многих моих страниц это было не страшно. У них были небольшие следы памяти, и не так много всего происходило. Но многие другие из этих страниц имели большие подробные графические изображения для иллюстрации. Вскоре обычное использование этого интерфейса операторами нашего оборудования вызвало огромные выделения памяти, которые никогда не очищались и в конечном итоге забивали все процессы на машине. После того, как стремительное развитие началось с цунами, а затем и приливов, я, наконец, решил устранить утечки памяти раз и навсегда. Я не буду вдаваться в подробности всех трюков, которые я реализовал, чтобы очистить память ( WeakReference s для изображений, отцепить обработчики событий в Unload (), используя специальный таймер, реализующий IWeakEventListener интерфейс и т.д ...). Ключевое изменение, которое я сделал, состояло в том, чтобы перейти к страницам, используя Uri вместо объекта ( NavigationService.Navigate Method (Uri) ). При использовании этого типа навигации есть два важных отличия:

  1. IsKeepAlive по умолчанию установлено на false.
  2. Теперь сборщик мусора попытается очистить навигационный объект, как если бы IsKeepAlive было установлено на false.

Так что теперь моя навигация выглядит так:

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

Здесь следует еще кое-что отметить: это влияет не только на то, как объекты очищаются сборщиком мусора, но и на то, как они изначально выделяются в памяти , как я скоро выясню.

Казалось, все работало отлично. Моя память быстро очищалась до моего начального состояния, когда я перемещался по страницам, интенсивно использующим графику, пока я не попал на эту конкретную страницу с конкретным вызовом dll dataSource, чтобы заполнить некоторые поля со списком. Тогда я получил это противное FatalEngineExecutionError. После нескольких дней исследований и нахождения неясных предложений или весьма специфических решений, которые не относились ко мне, а также использования почти всех средств отладки в моем личном арсенале программирования, я, наконец, решил, что единственный способ, которым я действительно собираюсь прибить это Это была крайняя мера восстановления точной копии этой конкретной страницы, элемент за элементом, метод за методом, строка за строкой, пока я наконец не наткнулся на код, выдавший это исключение. Это было так утомительно и больно, как я намекаю, но я, наконец, отследил это.

Оказалось, что неуправляемая dll выделяла память для записи данных в массивы, которые я отправляла для заполнения. Этот конкретный метод фактически проверяет номер параметра и, исходя из этой информации, выделяет массив определенного размера в зависимости от объема данных, которые он ожидает записать в отправленный мною массив. Код, вызвавший сбой:

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

Этот код может показаться идентичным приведенному выше примеру, но он имеет одно крошечное отличие. Размер массива, который я выделяю, равен 2 , а не 3 . Я сделал это, потому что знал, что этот конкретный ComboBox будет иметь только два элемента выбора, в отличие от других ComboBox на странице, у всех из которых было три элемента выбора. Однако неуправляемый код не видел вещи так, как я это видел. Он получил массив, который я передал, и попытался записать массив size [3] в мой размер [2], и на этом все. * bang! * * crash! * Я изменил размер выделения на 3, и ошибка исчезла.

Теперь этот код уже работал без этой ошибки, по крайней мере, год. Но простой переход на эту страницу через Uri в отличие от Object вызвал сбой. Это подразумевает, что исходный объект должен быть размещен по-другому из-за метода навигации, который я использовал. Поскольку с моим старым методом навигации память просто складывалась на место и оставлялась, как мне показалось, вечной, казалось, не имеет значения, была ли она немного повреждена в одном или двух небольших местах. Как только сборщик мусора должен был что-то сделать с этой памятью (например, очистить ее), он обнаружил повреждение памяти и выдал исключение. По иронии судьбы, моей главной утечкой памяти было сокрытие фатальной ошибки памяти!

Очевидно, что мы собираемся пересмотреть этот интерфейс, чтобы избежать таких простых предположений, вызывающих такие сбои в будущем. Надеюсь, что это поможет некоторым другим узнать, что происходит в их собственном коде.

3 голосов
/ 23 июня 2010

Презентация, которая может быть хорошим руководством о том, с чего начать с такого рода проблемами, выглядит следующим образом: Хардкорная отладка производства в .NET от Ingo Rammer .

Я немного программирую на C ++ / CLI, и повреждение кучи обычно не приводит к этой ошибке; обычно повреждение кучи либо приводит к повреждению данных и последующему нормальному исключению, либо к ошибке защиты памяти - что, вероятно, ничего не значит.

В дополнение к попытке .net 4.0 (которая загружает неуправляемый код по-разному), вы должны сравнить версии CLR для x86 и x64 - если это возможно - версия x64 имеет большее адресное пространство и, таким образом, совершенно другое поведение malloc (+ фрагментация) и так что вам может повезти и у вас будет другая (более отлаживаемая) ошибка (если она вообще возникает).

Кроме того, включили ли вы отладку неуправляемого кода в отладчике (вариант проекта), когда вы работаете с Visual Studio? А у вас есть Managed Debug Assistants?

2 голосов
/ 18 сентября 2017

В моем случае я установил обработчик исключений с AppDomain.CurrentDomain.FirstChanceException.Этот обработчик регистрировал некоторые исключения, и все было хорошо в течение нескольких лет (фактически этот код отладки не должен был оставаться в рабочем состоянии).

Но после ошибки конфигурации регистратор начал отказывать, и сам обработчикбросал, что, по-видимому, привело к FatalExecutionEngineError, казалось бы, из ниоткуда.

Таким образом, любой, кто столкнулся с этой ошибкой, мог потратить несколько секунд на поиск вхождений FirstChanceException в любом месте кода и, возможно, сэкономить несколько часовпочесать голову:)

0 голосов
/ 20 января 2013

Если вы используете thread.sleep (), это может быть причиной. Неуправляемый код может быть спит только из функции сна () kernell.32.

...