Различные способы утечки памяти

Основная концепция утечки памяти - это несоответствие новой операции/удаления во время выполнения кода из-за неправильной практики кодирования или в случае ошибок при выполнении операции удаления.

Но в последнее время меня спрашивали в интервью о других путях утечки памяти. Я не ответил на это. Что это?

Ответы

Ответ 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