Повреждение "кучи": Какова причина могла быть?

Реальная история (значения изменены для анонимности). Используйте, чтобы в приложении было неявное enum

public enum { orange, banana, mango }

Некоторый рефакторинг вокруг порядка и новых значений, и мы решили сделать это явным:

public enum { orange=1, banana=2, grape=3, mango=4 }

выглядит безобидным ...
Следующая вещь, веб-сайт взрывается. Почистите голову, проверьте сервис, добавьте отладочные сообщения, все в порядке.
Вниз по кроличьей норе, один день спустя, причиной является базовый сервис wcf, который использовал enum в качестве возвращаемого типа.

Очевидно, Wcf не любит перечисления без значения по умолчанию .

Исправлено:

public enum { wcfBugBane=0, orange=1, banana=2, grape=3, mango=4 }

Так что ... это может вас укусить.

19
задан arjuncc 19 October 2018 в 09:21
поделиться

11 ответов

Общие сценарии включают:

  • Запись вне выделенного пространства массива ( char * stuff = new char [10]; stuff [10] = 3; )
  • Приведение к неправильному типу
  • Неинициализированные указатели
  • Ошибка опечатки для -> и.
  • Ошибка опечатки при использовании * и & (или кратного любого из них)

[EDIT] Из комментариев, еще несколько:

  • Смешивание new [] и new с delete [] и delete
  • Отсутствующие или неправильные конструкторы копирования
  • Указатель, указывающий на мусор
  • Вызов удаления несколько раз на одних и тех же данных
  • Полиморфные базовые классы без виртуальных деструкторов
38
ответ дан 30 November 2019 в 01:56
поделиться

Добро пожаловать в ад. Нет простого решения, поэтому я приведу лишь несколько указателей.

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

В этом случае вы можете попробовать инструмент анализатора, такой как Purify. Они обнаружат почти все неприятности, которые делает ваш код, но также будут работать ОЧЕНЬ медленно. Такой инструмент будет обнаруживать доступ за пределами привязанной памяти, доступ к освобожденной памяти, попытки освободить дважды один и тот же блок, использование неправильного распределителя / освобождающего средства и т. Д. в самый неподходящий момент.

13
ответ дан 30 November 2019 в 01:56
поделиться

Существуют продукты, которые будут наблюдать за выделением и освобождением памяти, а также создавать отчет об аномалиях. Последний раз я использовал один, они были недешевыми, но я не знаю, какова сейчас ситуация. Однако поиск материалов для VC ++ 6 может быть проблемой.

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

Я бы посоветовал везде использовать std :: tr1 :: smart_ptr <> вместо необработанных указателей, но я не уверен, что VC ++ 6 будет поддерживать это.

Почему? вы все еще на VC ++ 6? Было бы целесообразно обновиться? Инструменты лучше с более свежими версиями, и они полностью поддерживают интеллектуальные указатели.

6
ответ дан 30 November 2019 в 01:56
поделиться

Вы можете посмотреть пример главы из книги Advanced Windows Debugging , в которой приведены различные примеры повреждения кучи. .

РЕДАКТИРОВАТЬ: Если вы используете контейнеры stl, которые могут перемещать элементы во время изменений (например, vector, deque), убедитесь, что вы не сохраняете ссылки на такие элементы между операциями, которые могут его изменить.

6
ответ дан 30 November 2019 в 01:56
поделиться

Ознакомьтесь с ответами на этот вопрос, связанный с .

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

По крайней мере, принцип должен быть таким же.

3
ответ дан 30 November 2019 в 01:56
поделиться

Распространенная ошибка - освободить () или удалить более одной выделенной памяти. Может помочь вставить что-то вроде * var = NULL после таких вызовов и проверить наличие! = NULL при бесплатном вызове. Хотя в C ++ разрешен вызов delete с переменной NULL, вызов C - free () завершится неудачно.

Также распространенной проблемой является путаница delete и delete [].

Переменные, выделенные с помощью new , должны быть освобождены с помощью delete .

Массив, выделенный с помощью new [] , должен быть освобожден с помощью delete [] .

Также не смешивайте управление памятью в стиле C (malloc, calloc, free) с управлением памятью в стиле C ++ (new / delete). В унаследованном коде часто оба смешаны, но вещи, выделенные одним, не могут быть освобождены с помощью другого.

2
ответ дан 30 November 2019 в 01:56
поделиться

Каждый раз, когда вы что-то делаете который не определен в языковом стандарте, это неопределенное поведение, и одним из способов его проявления является повреждение кучи. В C ++ есть около трех миллионов способов сделать это, так что ' Сказать действительно невозможно.

Несколько распространенных случаев - это двойное освобождение динамически выделяемой памяти или запись за пределами массива. Или запись в неинициализированный указатель.

В последних версиях компилятора Microsoft добавлен переключатель компилятора / analysis, который выполняет ряд статических анализов для обнаружения таких ошибок. В Linux, valgrind - очевидный выбор.

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

Если возможно, вам следует обновить до подходящего компилятора.

Компилятор s добавляет переключатель компилятора / analysis, который выполняет ряд статического анализа для обнаружения таких ошибок. В Linux, valgrind - очевидный выбор.

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

Если возможно, вам следует обновить до подходящего компилятора.

Компилятор s добавляет переключатель компилятора / analysis, который выполняет ряд статического анализа для обнаружения таких ошибок. В Linux, valgrind - очевидный выбор.

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

Если возможно, вам следует обновить до подходящего компилятора.

3
ответ дан 30 November 2019 в 01:56
поделиться

Дополнительный совет по отладке - посмотреть на значения, которые записываются в неправильное место, с использованием представления необработанной памяти. Он пишет нули ... строки ... какой-то другой узнаваемый шаблон чисел? Если вы видите поврежденную память в какой-то момент после возникновения ошибки, это может дать намек на код, который ее вызвал.

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

3
ответ дан 30 November 2019 в 01:56
поделиться

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

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
}
2
ответ дан 30 November 2019 в 01:56
поделиться

Если у вас есть доступ к машине * nix можно использовать Valgrind.

1
ответ дан 30 November 2019 в 01:56
поделиться

Самая сложная ошибка повреждения памяти, с которой я столкнулся: (1) вызов функции в DLL, которая вернула std :: vector , а затем (2) разрешение что std :: vector выпадает из области видимости (что, по сути, и составляет весь смысл std :: vector ). К сожалению, оказалось, что DLL была связана с одной версией среды выполнения C ++, а программа была связана с другой; это означало, что библиотека вызывала одну версию new [] , а я вызывал совершенно другую версию delete [] .

Здесь происходит не это, потому что терпел неудачу каждый раз, и, согласно одному из ваших комментариев, «ошибка проявляется в сбое один раз в миллионный раз». Я бы предположил, что там ' s оператор if , который выполняется один раз из миллиона и вызывает ошибку двойного удаления .

Недавно я использовал ознакомительные версии двух продуктов, которые могут вам помочь: IBM Rational Purify и Intel Parallel Inspector . Я уверен, что есть и другие (много упоминается Insure ++). В Linux вы должны использовать Valgrind.

1
ответ дан 30 November 2019 в 01:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: