Различные способы утечки памяти - PullRequest
15 голосов
/ 03 июля 2011

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

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

Ответы [ 7 ]

16 голосов
/ 03 июля 2011

Общие проблемы с динамической памятью:

  • Динамическое выделение памяти с new и без освобождения с delete.
  • Динамическое выделение памяти с new[] и освобождение с delete.
  • Динамическое выделение памяти new и освобождение его с помощью free.
  • Динамическое выделение памяти malloc и освобождение его с помощью delete.

В дополнение к утечкам памяти / повреждению памяти последние 3 сценария вызовут страшное неопределенное поведение .

Вот еще несколько возможных сценариев утечки памяти, которые я могу вспомнить:

  • Если указателю, указывающему на динамически выделяемую область памяти, перед удалением будет присвоено новое значение, это приведет к висячему указателю и утечка памяти.

Пример кода:

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 берут на себя ответственность за удаление содержащегося объекта , только если это не указатель типа .
Нужно явно пройти через контейнер и удалить каждый содержащийся тип перед удалением самого контейнера. В противном случае возникает утечка памяти.
Здесь является примером такого сценария.

- Деструктор не виртуального базового класса

Удаление указателя на базовый класс, который указывает на любой динамически размещенный объект производного класса в куче. Это приводит к неопределенному поведению.

Пример кода:

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, как в примере выше, приведет к утечке памяти и неопределенному поведению.

4 голосов
/ 03 июля 2011

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

Любая память, которая сохраняется за пределами последней точки, в которой она необходима, может считаться «утекшей».Эта память может в конечном итоге быть освобождена вручную с удалением дальше в коде, что делает эти утечки временными, а не постоянными утечками, с которыми вы сталкиваетесь при несовпадающих операторах new / delete.Хотя в течение времени, в течение которого сохраняется «утечка», чистый эффект остается тем же.Вы уменьшаете количество доступных ресурсов (памяти) для других частей вашей программы.

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

3 голосов
/ 03 июля 2011
  1. Выделение памяти с помощью new/new[]/malloc и не освобождение ее
  2. Выделение памяти для некоторого указателя и ее случайная перезапись, например p = new int; p = new int[1];
  3. Выделение памяти в воздухе.т.е. new int[100]; или cout<<(*new string("hello"))<<endl;
2 голосов
/ 03 июля 2011

Всегда есть мой любимый вариант - «утечка памяти без утечек», когда ваша программа правильно сохраняет указатели на выделенную память, но (по какой-либо причине) фактически не может освободить память, на которую они указывают:

// 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.

1 голос
/ 03 июля 2011

Другой сценарий - использование интеллектуальных указателей подсчета ссылок, таких как boost::shared_ptr, которые обычно считаются «устранением утечек памяти», но вы создаете циклические ссылки.

Как избежать утечки памяти с помощьюshared_ptr?

1 голос
/ 03 июля 2011

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

0 голосов
/ 03 июля 2011

Я использую сборщик мусора Boehm с перегруженным оператором new и delete, который отлично работает для любого класса, который был скомпилирован с ним, но ужасно дает сбой с std :: string и несколькими контейнерами STL, даже если переменные члены класса мусора. Было несколько утечек памяти, пока я не понял это. К счастью, это обеспечивает сборщик мусора.

Также я помню автомобиль с автоматическим управлением, который был запрограммирован на Java, но падал * каждые 20-40 минут. Он собирал информацию об обнаружении объектов о различных подлесках и мусоре, с которыми он сталкивался вдоль дороги, подписывал их на какую-то очередь и ... и никогда не удалял их.

*: Смотри, что я там делал: D

...