Недавно я столкнулся с повреждением управляемой кучи, что было для меня чем-то новым. Я был очень разочарован этим и должен был выучить много вещей, чтобы иметь возможность отладить его. Я хочу поблагодарить Сева Титова, который дал мне правильное направление для начала. Его ответ был кратким и очень полезным. Я хочу записать действия, предпринятые для устранения проблемы, для собственного ознакомления. Возможно, это будет полезно для тех, кто плохо знаком с этим.
Отладка повреждения кучи в .NET 4:
Как заподозрить повреждение кучи?
Коротко:
Приложение аварийно завершает работу безотносительно к отлову примененных исключений и даже проходит через одеяла типа catch(Exception)
, которые должны перехватывать все исключения.
Изучение стека CLR в дампах аварийного завершения приложения показывает сборщик мусора в верхней части стека:
000000001dabd8c8 000007feea129a1d [**HelperMethodFrame**: 000000001dabd8c8]
000000001dabda00 000007fee90cfce8 System.Text.StringBuilder.ExpandByABlock(Int32)
000000001dabda40 000007fee90cfba4 System.Text.StringBuilder.Append(Char*, Int32)
...
EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 000007feea129a1d (**clr!WKS::gc_heap**::find_first_object+0x0000000000000092)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 0000000000000000
Parameter[1]: 0000000000003d80
...
Стек CLR всегда показывает разные точки. Произошел ли сбой или показанный код явно не имеет значения, как метод StringBuilder, который, как показано, вызывает исключение.
Для получения дополнительной информации см. .NET Crash: Повреждение управляемой кучи, вызывающее неуправляемый код .
Идем шаг за шагом. Каждый следующий шаг используется, если предыдущий не помогает.
Шаг 1. Проверьте код.
Проверьте код на предмет использования небезопасных или собственных кодов:
- Просмотрите код для
unsafe
, DllImport
операторов.
- Загрузите .NET Reflector и используйте его для анализа сборок приложений на предмет
PInvoke
. Аналогичным образом проанализируйте сторонние сборки, используемые приложением.
Если обнаружено небезопасное использование или использование собственного кода, обратите на них особое внимание. Наиболее распространенной причиной повреждения кучи в таких случаях является переполнение буфера или несоответствие типа аргумента. Убедитесь, что буфер, предоставленный нативному коду для заполнения, достаточно большой и что все аргументы, передаваемые нативному коду, имеют ожидаемый тип.
Шаг 2. Проверьте, можно ли перехватить это поврежденное состояние .
Для обработки таких исключений необходимо украсить метод, содержащий оператор catch(Exception)
, с атрибутом [HandleProcessCorruptedStateExceptions]
или применить следующее в файле app.config
:
<configuration>
<runtime>
<legacyCorruptedStateExceptionsPolicy enabled="true" />
</runtime>
</configuration>
В случае, если исключение было успешно перехвачено, вы можете войти и просмотреть его. Это означает, что это не проблема поврежденной кучи.
Поврежденные исключения кучи не могут быть обработаны вообще: HandleProcessCorruptedStateExceptions, кажется, не работает .
Дополнительная информация об исключениях из-за поврежденного состояния приведена в Все об исключениях из-за поврежденного состояния в .NET4 .
Шаг 3. Живая отладка.
На этом шаге мы отлаживаем аварийное приложение вживую в производственной среде (или там, где мы можем воспроизвести аварию).
Загрузка Средства отладки для Windows из Microsoft Windows SDK для Windows 7 и .NET Framework 4 (будет загружен веб-установщик, который позволит выбрать необходимые компоненты для установки - отметьте все компоненты). Он установит 32- и 64-битные (если ваша система x64) версии необходимых инструментов отладки.
Здесь нужно знать, как подключить WinDbg к живому процессу, как получить аварийные дампы и изучить их, как загрузить расширение SOS в WinDbg (подробности в Google).
Включить помощники по отладке:
Запустите проверку приложения (C:\Program Files\Application Verifier
- используйте требуемую редакцию, x86 или x64, в зависимости от режима компиляции исполняемого файла), добавьте туда свой исполняемый файл на левой панели и на правой панели проверьте один узел "Основы" / Кучи ". Сохраните изменения.
Запустить помощник Global Flags (C:\Program Files\Debugging Tools for Windows\gflags.exe
- снова выбрать правильное издание, x86 или x64).После запуска Global Flags перейдите на вкладку «Файл изображения» и в верхнем текстовом поле введите имя исполняемого файла без каких-либо путей (например, «MyProgram.exe»).Затем нажмите клавишу Tab и установите следующие поля:
- Включить проверку хвоста кучи
- Включить проверку без кучи
- Включить проверку параметра кучи
- Включить проверку кучи при вызове
- Отключить объединение кучи бесплатно
- Включить кучу страницы
- Включить теги кучи
- Включить верификатор приложения
- Отладчик (введите путь к установленной WinDbg в текстовое поле справа, например,
C:\Program Files\Debugging Tools for Windows (x64)\windbg.exe -g
).
Для получения дополнительной информации см. Повреждение кучи, часть 2 .
Перейдите в «Панель управления / Система и безопасность / Система» (или щелкните правой кнопкой мыши «Компьютер» в меню Пуск ивыберите «Свойства». Там нажмите «Дополнительные параметры системы», в появившемся диалоговом окне перейдите на вкладку «Дополнительно» и нажмите кнопку «Переменные среды». В появившемся диалоговом окне добавьте новую системную переменную (если вы являетесь системным администратором).- переменная пользователя в противном случае - вам нужно выйти / лОгин в этом случае).Обязательной переменной является «COMPLUS_HeapVerify» со значением «1».Более подробную информацию можно найти в вопросе переполнения стека .NET / C #: как установить переменную среды отладки COMPLUS_HeapVerify? .
Теперь мы находимсяготов начать отладку.Запустите приложение.WinDbg должен запуститься автоматически для этого.Оставьте приложение работающим до тех пор, пока оно не попадет в WinDgb, а затем проверьте дамп.
TIP : для быстрого удаления Global Flags , Application Verifier иВ настройках вложений отладчика удалите следующий ключ в реестре: x64 - HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\*YourAppName*
Шаг 4. Включите MDAs.
Попробуйте использовать помощники по управляемой отладке.Подробности в вопросе переполнения стека Какие MDA полезны для отслеживания повреждения кучи? .
MDA должны использоваться вместе с WinDbg.Я использовал их даже вместе с Global Flags и Application Verifier .
Шаг 5. Включите GCStress.
Использование GCStressэто крайний вариант, потому что приложение становится практически непригодным для использования, но это все еще путь.Более подробная информация находится в GCStress: Как включить в Windows 7? .
Шаг 6. Компиляция для x86.
Если ваше приложение в настоящее время компилируется для платформы «Любой процессор» или «x64», попробуйте скомпилировать его для «x86», если для вас нет разницы, какую платформу использовать.Я видел, как это сообщается, чтобы решить проблему для кого-то.
Шаг 7. Отключить одновременный сборщик мусора - это то, что мне помогло
Сообщается, что известна проблема в.NET 4 сообщается в потоке Нарушение прав доступа в .NET 4 Runtime в gc_heap :: garbage_collect без неуправляемых модулей .Эту проблему можно решить, отключив одновременный сборщик мусора в файле app.config
:
<?xml version="1.0"?>
<configuration>
<runtime>
<gcConcurrent enabled="false" />
</runtime>
</configuration>