Различные способы утечки памяти
Основная концепция утечки памяти - это несоответствие новой операции/удаления во время выполнения кода из-за неправильной практики кодирования или в случае ошибок при выполнении операции удаления.
Но в последнее время меня спрашивали в интервью о других путях утечки памяти.
Я не ответил на это. Что это?
Ответы
Ответ 1
Общие проблемы с динамической памятью:
- Динамическое распределение памяти с помощью
new
и не освобождение от delete
.
- Динамическое распределение памяти с помощью
new[]
и освобождение с помощью delete
.
- Распределение динамической памяти
new
и освободить ее с помощью free
.
- Распределение динамической памяти
malloc
и освободите ее с помощью delete
.
В дополнение к утечкам памяти/повреждению памяти последние 3 сценария вызовут страшный Undefined Поведение.
Несколько других потенциальных проблем с утечкой памяти, которые я могу вспомнить, следующие:
- Если указатель, указывающий на динамически выделенную область памяти, переопределяет новое значение перед его освобождением, это приведет к зависанию указатель и утечка памяти.
Пример кода:
char *a = new[128];
char *b = new[128];
b = a;
delete[]a;
delete[]b; // will not deallocate the pointer to the original allocated memory.
- указатели в контейнерах STL
Более распространенным и часто встречающимся сценарием является сохранение указателей, указывающих на динамически распределенные типы в контейнерах STL. Важно отметить, что контейнеры STL берут на себя ответственность за удаление содержащегося объекта только в том случае, если он не является типом указателя.
Нужно явно прокручивать контейнер и удалять каждый содержащийся тип перед удалением самого контейнера. Это не приводит к утечке памяти.
Здесь - пример такого сценария.
- проблема деструктора не виртуального базового класса
Удаление указателя на класс Base, который указывает на любой динамически выделенный объект производного класса в куче. Это приводит к поведению Undefined.
Пример кода:
class MyClass
{
public:
virtual void doSomething(){}
};
class MyClass2 : public MyClass
{
private:
std::string str;
public: MyClass2( std::string& s)
{
str=s;
}
virtual void doSomething(){}
};
int main()
{
std::str hello("hello");
MyClass * p = new MyClass2(hello);
if( p )
{
delete p;
}
return 0;
}
В этом примере вызывается только деструктор MyClass:: ~ MyClass(), и MyClass2:: ~ MyClass2() никогда не вызывается. Для соответствующего освобождения необходимо будет
MyClass::virtual ~MyClass(){}
- Вызов delete
по указателю void
Пример кода:
void doSomething( void * p )
{
//do something interesting
if(p)
delete p;
}
int main()
{
A* p = new A();
doSomething(p);
return 0;
}
Вызов delete
по указателю void
, как в приведенном выше примере, вызовет утечку памяти и поведение Undefined.
Ответ 2
В качестве вопроса интервью интервьюер, возможно, искал более широкую перспективу, чем несоответствие нового/исключенного учебника.
Любая память, которая сохраняется за последней точкой, которая необходима, может считаться "утечкой". Впоследствии эта память может быть освобождена вручную с удалением в коде ниже, что делает эти утечки временными, а не постоянными утечками, с которыми вы сталкиваетесь с несовпадающими операциями new/delete. В течение времени, когда "утечка" сохраняется, чистый эффект остается неизменным. Вы уменьшаете количество доступных ресурсов (памяти) в другие части вашей программы.
В собранном мусором кодеке память считается утечкой, если вы продолжаете удерживать какие-либо ссылки на объект, который вам больше не нужен, тем самым предотвращая его сборщик мусора. Если вы держите ненужные объекты на неопределенный срок, вы создали постоянную утечку в собранном мусором коде.
Ответ 3
- Выделение памяти с помощью
new/new[]/malloc
и не освобождая его
- Выделение памяти на определенный указатель
и перезаписывать его случайно, например.
p = new int; p = new int[1];
- Выделение памяти в воздухе. то есть
new int[100];
или cout<<(*new string("hello"))<<endl;
Ответ 4
Всегда моя любимая "утечка утечки памяти", где ваша программа правильно сохраняет указатели на выделенную память, но (по какой-либо причине) никогда не обходит вокруг, чтобы освободить память, на которую они указывают:
// Maybe not technically a memory leak, but it might as well be one
static vector<const char *> strings;
void StoreTheString(const char * str)
{
strings.push_back(strdup(str));
}
Это вдвойне раздражает, потому что программы обнаружения утечки памяти не распознают его как утечку памяти (потому что указатели по-прежнему доступны), и тем не менее ваша программа, тем не менее, будет потреблять память, пока она не сработает.
Такая проблема может возникать даже в сборках, собранных с помощью мусора, таких как Java.
Ответ 5
Фрагментация может быть одной из проблем, которые вызывают нехватку памяти.
После продолжительного запуска ваших программ это может привести к фрагментации памяти.
Ответ 6
Другим сценарием является использование интеллектуальных указателей подсчета ссылок, таких как boost::shared_ptr
, которые обычно считаются "устраняющими утечки памяти", но вы создаете циклические ссылки.
Как избежать утечки памяти с помощью shared_ptr?
Ответ 7
Я использую сборщик мусора Boehm с перегруженным оператором new и удаляю, и который отлично работает для любого класса, который был скомпилирован с ним, но неудачно работает с std::string и несколькими контейнерами STL, даже если переменные являются членами из собранного мусора класса. Произошло несколько утечек памяти, пока я не осознал это. К счастью, он предоставляет сборщик мусора собрал.
Также я помню самозанятый автомобиль, который был запрограммирован на Java, но разбился * каждые 20-40 минут. Он собирал информацию об обнаружении объекта о различных кусках подлеска и мусора, которые он встречал по дороге, подписывался на какую-то очередь,... и никогда не удалял их.
*: Посмотрите, что я там сделал: D