Утечки памяти в C++: что это такое и чем они опасны

Разбираемся в трудно уловимых уязвимостях приложений, чтобы всё работало гладко и без тормозов.

Утечка памяти (англ. memory leak) — это неконтролируемое уменьшение свободной оперативной или виртуальной памяти компьютера. Причиной утечек становятся ошибки в программном коде.

В этой статье мы поймём, как появляются утечки и как их устранять.

Как появляются утечки памяти

Любые программы используют в своей работе память, чтобы хранить какие-то данные. В C++ и многих других языках память динамическая. Это значит, что операционная система при запуске программы резервирует какое-то количество ячеек в ОЗУ, а потом выделяет новые, если они нужны.

Создали переменную для числа (int)? Вот тебе 16 бит (4 байта). Нужен массив из ста элементов для больших чисел (long)? Вот тебе ещё 3200 бит (800 байт).

Когда программисту уже не нужен какой-то массив или объект, он должен сказать системе, что его можно удалить с помощью оператора delete[] и освободить память.

Евгений Кучерявый

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


//Создаём пустой указатель
double *pointer;
 
//Создаём массив и получаем ссылку на него
pointer = new double[15];
 
//Указываем тестовое значение для первой ячейки
pointer[0] = 5;
 
//Выведет адрес ячейки и 5, так как по адресу находится первый элемент массива
cout << "Address: " << pointer << " | Value: " << *pointer << "\n";
 
delete [] pointer;
cout << "Deleted\n";
 
//Адрес остался прежним, но значение будет 0, так как память была очищена
cout << "Address: " << pointer << " | Value: " << *pointer << "\n";

Вот результат:

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

На примере в цикле десять раз создаётся новый массив, а его адрес записывается в указатель. Адреса старых массивов при этом удаляются. Поэтому дальше оператор delete[] удаляет только последний созданный массив. Остальные останутся в памяти до тех пор, пока не будет закрыта программа.

Чем опасны утечки памяти

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

Например, приложение может работать с каким-нибудь файлом непосредственно перед закрытием. В этом случае файл будет повреждён. Последствия возможны самые разные: от нервного срыва пользователя, если это была презентация, над которой он работал несколько дней, до поломки системы, если это был очень важный файл.

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

Может показаться, что раз это «утечка», то что-то случится с вашими данными. На самом деле утекает именно свободная память, а не её содержимое.

Как бороться с утечками памяти

Если у вас есть доступ к исходникам, то изучите код, чтобы определить, нет ли там утечек. Вручную делать это бессмысленно, особенно если проект большой, поэтому обратимся к отладчику использования памяти (англ. memory debugger).

Если вы пользуетесь IDE вроде Visual Studio, то там должен быть встроенный отладчик. Есть и сторонние инструменты вроде GDB или LLDB. Отладчик покажет, какие данные хранит программа и к каким ячейкам имеет доступ.

И будьте осторожнее с указателями:

Указатель на указатель на указатель на переменную X

Если доступа к коду у вас нет, но нужна библиотека, в которой есть утечка, то её стоит вынести в отдельную программу (B). Ваша основная программа (A) запустит программу B, чтобы вызвать какую-то функцию. После этого программа B будет закрываться, чтобы освободить всю использованную (в том числе и утёкшую) память.

Если же функционал из библиотеки нужен постоянно, то программу B можно оставить работать, но перезапускать её с определённой периодичностью — до того как она сожрёт слишком большой объём памяти.

Конечно, лучше избегать таких библиотек, но мы живём в неидеальном мире — иногда альтернатив попросту нет.

В более высокоуровневых языках вроде C# или Java существуют сборщики мусора (англ. garbage collector). Это специальный процесс, который сканирует память и удаляет те ячейки, которые уже не нужны приложению.

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

Впрочем, иногда от утечек не спасает и сборщик мусора.

Заключение

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

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

Курс

Профессия
Разработчик на C++ с нуля


Вы пройдёте полный курс по С++ и прикладной курс по Unreal Engine 4. Вы научитесь работать с многопоточностью, использовать инструменты и средства разработки: Git, GCC, GDB. Вам будет проще найти работу программиста в геймдеве.

Хочешь получать крутые статьи по программированию?
Подпишись на рассылку Skillbox