Повреждение кучи: что может быть причиной?
Я расследую крах из-за кучи. Поскольку эта проблема является нетривиальной и включает в себя анализ результатов стека и дампа, я решил сделать обзор кода файлов, связанных с сбоем.
Чтобы быть откровенным, у меня нет глубоких знаний о том, когда куча может быть повреждена.
Я был бы признателен, если бы вы предложили сценарии, которые могут привести к куче коррупции.
Платформа: Windows XP
Язык: С++
Компилятор: VC6
Заранее спасибо
SSS
Ответы
Ответ 1
Общие сценарии включают:
- Запись вне выделенного пространства массива (
char *stuff = new char[10]; stuff[10] = 3;
)
- Кастинг с неправильным типом
- Неинициализированные указатели
- Ошибка Typo для → и.
- Ошибка при использовании * и и (или нескольких).
[EDIT] Из комментариев еще несколько:
- Смешивание нового [] и нового с удалением [] и удаление
- Отсутствующие или неправильные конструкторы копирования
- Указатель, указывающий на мусор
- Вызов несколько раз удаляться по тем же данным
- Полиморфные базовые классы без виртуальных деструкторов
Ответ 2
Добро пожаловать в ад. Нет простого решения, поэтому я буду предлагать только некоторые указатели.
Попробуйте воспроизвести ошибку в отладочной среде. Отладчики могут размещать ваши распределения кучи связанными проверками и сообщают вам, были ли вы написаны в этих связанных проверках. Кроме того, он будет последовательно распределять память, используя одни и те же виртуальные адреса, что упрощает репродуцирование.
В этом случае вы можете попробовать инструмент анализатора, например Purify. Они обнаружат почти все, что противно, что ваш код делает, но также будет работать ОЧЕНЬ медленно. Такой инструмент обнаруживает доступ к связанной памяти, освобождает доступ к памяти, пытается освободить дважды тот же блок, используя неправильный распределитель/дезактиваторы и т.д. Это все виды условий, которые могут оставаться скрытыми в течение очень долгого времени и только сбой в самый неподходящий момент.
Ответ 3
Вы можете посмотреть примерную главу из Расширенная книга отладки Windows, которая содержит различные примеры повреждения кучи.
EDIT: если вы используете stl-контейнеры, которые могут перемещать элементы во время изменений (т.е. vector, deque), убедитесь, что вы не храните ссылки на такие элементы в разных операциях, которые могут его изменить.
Ответ 4
Есть продукты, которые будут наблюдать за распределением памяти и освобождением памяти, а также сообщать об аномалиях. Последнее, что я использовал, они были не дешевы, но я не знаю, какова ситуация сейчас. Однако найти материал для VС++ 6 может быть проблемой.
Помните, что вы можете получать кучу коррупции намного чаще, чем вы собираетесь разбиться, поэтому будьте внимательны к отчетам о проблемах и исправьте все повреждения кучи.
Я предлагаю использовать std::tr1::smart_ptr<>
вместо необработанных указателей везде, но я не уверен, что VС++ 6 будет поддерживать это.
Почему вы все еще на VС++ 6? Было бы целесообразно обновить? Инструменты лучше с более поздними версиями, и они полностью поддерживают интеллектуальные указатели.
Ответ 5
Проверьте ответы на этот связанный с этим вопрос.
Ответ . Я предложил метод, который может вернуть вас к коду, который фактически вызывает повреждение кучи. Мой ответ описывает технику с помощью gdb
, но я уверен, что вы должны иметь возможность делать что-то подобное в окнах.
Принцип, по крайней мере, должен быть одним и тем же.
Ответ 6
Каждый раз, когда вы делаете что-то, что не определено в стандарте языка, это поведение undefined, и одним из способов его проявления может быть повреждение кучи. В С++ существует около трех миллионов способов сделать это, поэтому на самом деле невозможно сказать.
Несколько распространенных случаев - это двойное освобождение динамически распределенной памяти или запись за пределами массива. Или записать неинициализированный указатель.
Недавние версии компилятора Microsoft добавляют компилятор/анализатор компилятора, который выполняет кучу статического анализа для обнаружения таких ошибок. В Linux, valgrind - очевидный выбор.
Конечно, вы используете VC6, который не поддерживается в течение многих лет, и который имеет ряд известных ошибок, в результате чего генерируется недействительный код.
Если возможно, вам следует перейти на правильный компилятор.
Ответ 7
Дополнительная подсказка для отладки - это посмотреть на значения, которые записываются в неправильном месте, используя представление необработанной памяти. Является ли это написанием нулей... строк... какой-нибудь другой узнаваемый шаблон числа? Если вы видите поврежденную память в какой-то момент после возникновения ошибки, это может дать подсказку кода, вызвавшего ее.
Также всегда устанавливайте указатели на нуль после удаления их даже в деструкторах. Иногда в цепочке деструкторов в базовом классе могут запускаться неожиданные вещи, а не вызвать доступ к частично удаленному подклассу.
Ответ 8
Его распространенная ошибка - освободить() или удалить выделенную память более чем на одну. Это может помочь вставить что-то вроде * var = NULL после таких вызовов и проверить!= NULL при вызове бесплатного. Хотя в С++ его законно вызывать удалить с помощью переменной NULL, вызов C - free() завершится с ошибкой.
Также распространенная проблема заключается в том, чтобы путать удаление и удаление [].
Переменные, выделенные с помощью new, должны быть освобождены с помощью удалить.
Массив, выделенный с помощью new [], должен быть освобожден с помощью delete [].
Также не следует смешивать управление памятью в стиле C (malloc, calloc, free) с управлением памятью стиля С++ (новый/удалить). В устаревшем коде часто оба смешаны, но вещи, выделенные с помощью одного, не могут быть освобождены вместе с другим.
Все эти ошибки обычно не распознаются компилятором.
Ответ 9
Во время освобождения памяти кучи сначала необходимо удалить первый блок памяти, а затем блок памяти родительского блока, иначе блок-блок дочернего модуля будет пропущен, что приведет к сбою после запуска инструмента в миллионы раз.
например:
constQ= new double* [num_equations];
for(int i=0;i<num_equations;i++)
{
constQ[i]=new double[num_equations];
for(int j=0;j<num_equations;j++)
{
constQ[i][j]=0.0;
}
.
.
.
//Deleting/Freeing memory block
//Here the below only parent memory block is deleted, the child memory block is leaked.
if(constQ!=NULL)
{
delete[] constQ;
constQ=NULL
}
//Correct way of deleting heap memory..First delet child block memory and then Parent block
if(constQ!=NULL)
{
for(int i=0; i <num_equations;i++)
{
delete[] constQ[i];
delete[] constQ;
constQ=NULL
}
Ответ 10
Если у вас есть доступ к машине * nix, вы можете использовать Valgrind.
Ответ 11
Самая сложная ошибка с повреждением памяти, с которой я столкнулся, (1) вызывает функцию в DLL, которая возвращает std::vector
, а затем (2), позволяя этому std::vector
выпасть из области действия (что в основном является целым точка std::vector
). К сожалению, оказалось, что DLL была связана с одной версией среды выполнения С++, и программа была связана с другой; что библиотека вызывала одну версию new[]
, и я вызывал совершенно другую версию delete[]
.
Это не то, что происходит здесь, потому что это терпит неудачу каждый раз и, согласно одному из ваших комментариев, "ошибка проявляется в результате краха в миллионный раз". Я бы предположил, что существует инструкция if
, которая берется раз в миллион раз и вызывает двойную ошибку delete
.
Недавно я использовал оценочные версии двух продуктов, которые могут вам помочь: IBM Rational Purify и Intel Parallel Inspector. Я уверен, что есть другие (Insure ++ упоминается много). В Linux вы бы использовали Valgrind.
Ответ 12
Вы считали, что изолировать источник коррупции с помощью gflags?
После того, как у вас есть дамп (или разрыв отладчика → WinDBG), вы можете увидеть, где коррупция вызвана более точно.
Вот некоторые примеры gflag:
http://blogs.msdn.com/b/webdav_101/archive/2010/06/22/detecting-heap-corruption-using-gflags-and-dumps.aspx
Приветствия, Seb
Ответ 13
Это синтаксис HeapAlloc fuction.
LPVOID WINAPI HeapAlloc(
_In_ HANDLE hHeap,
_In_ DWORD dwFlags,
_In_ SIZE_T dwBytes
);
Здесь dwFlags
параметр может иметь либо HEAP_GENERATE_EXCEPTIONS
, либо HEAP_NO_SERIALIZE
или HEAP_ZERO_MEMORY
.
В нашем файле мы должны проверить флаги, которые мы установили.
Если мы установили значение флага как HEAP_NO_SERIALIZE
, тогда не будет сериализации, которая означает, что несколько потоков будут обращаться к ресурсам, которые могут вызвать повреждение памяти.
"Установка значения HEAP_NO_SERIALIZE
исключает взаимное исключение из кучи. Без сериализации,
два или более потока, которые используют один и тот же дескриптор кучи, могут попытаться выделить или освободить память одновременно,
вероятно, вызывая коррупцию в куче ".
поэтому я думаю, что из-за повреждения памяти в куче, node получил сбой.