Введение

В информатике утечка памяти — это тип утечки ресурсов, возникающая, когда компьютерная программа некорректно управляет выделением памяти, в результате чего память, которая больше не требуется, не освобождается. Утечка памяти может также произойти, когда объект хранится в памяти, но становится недоступным для выполняющегося кода (то есть недостижимой памятью). Утечка памяти проявляется симптомами, схожими с рядом других проблем, и обычно может быть диагностирована только программистом, имеющим доступ к исходному коду программы. Связанным понятием является "избыточное потребление памяти" (или "прожорливость памяти"), когда программа потребляет чрезмерный объем памяти, но в конечном итоге всё же освобождает её. Поскольку утечки памяти могут исчерпать доступную системную память по мере работы приложения, они часто являются причиной или способствующим фактором старения программного обеспечения.

Проблемы программирования

Утечки памяти — распространенная ошибка в программировании, особенно при использовании языков, не имеющих встроенного автоматического сборщика мусора, таких как C и C++. Обычно утечка памяти возникает, когда динамически выделенная память становится недостижимой. Распространенность ошибок, связанных с утечками памяти, привела к разработке ряда инструментов отладки для обнаружения недостижимой памяти. BoundsChecker, Deleaker, Memory Validator, IBM Rational Purify, Valgrind, Parasoft Insure++, Dr. Memory и memwatch — одни из наиболее популярных отладчиков памяти для программ на C и C++. Функциональность "консервативного" сборщика мусора может быть добавлена в любой язык программирования, в котором она отсутствует как встроенная функция, и для программ на C и C++ доступны библиотеки для этого. Консервативный сборщик находит и освобождает большую часть, но не всю, недостижимой памяти. Хотя менеджер памяти может освободить недостижимую память, он не может освободить память, которая все еще достижима и, следовательно, потенциально полезна. Современные менеджеры памяти предоставляют программистам механизмы для семантической маркировки памяти с различными уровнями полезности, которые соответствуют различным уровням достижимости. Менеджер памяти не освобождает объект, который является сильно достижимым. Объект считается сильно достижимым, если он достижим либо непосредственно через сильную ссылку, либо косвенно через цепочку сильных ссылок. (Сильная ссылка — это ссылка, которая, в отличие от слабой ссылки, предотвращает сбор мусора.) Чтобы избежать этого, разработчик несет ответственность за очистку ссылок после использования, обычно путем установки ссылки в null, когда она больше не нужна, и, при необходимости, путем отмены регистрации любых прослушивателей событий, которые поддерживают сильные ссылки на объект. В целом, автоматическое управление памятью более надежно и удобно для разработчиков, поскольку им не нужно реализовывать процедуры освобождения или беспокоиться о последовательности выполнения очистки или о том, ссылается ли на объект что-либо. Программисту легче определить, когда ссылка больше не нужна, чем определить, когда объект больше не имеет ссылок. Однако автоматическое управление памятью может создавать накладные расходы на производительность и не устраняет все ошибки программирования, вызывающие утечки памяти.

Эффекты

Если программа имеет утечку памяти и её использование памяти неуклонно возрастает, обычно не проявляется немедленных симптомов. Любая физическая система обладает конечным объёмом памяти, и если утечка памяти не будет устранена (например, перезапуском программы с утечкой), она в конечном итоге вызовет проблемы. Большинство современных настольных операционных систем используют как оперативную память, физически расположенную в микросхемах RAM, так и вторичную память, такую как жесткий диск. Распределение памяти происходит динамически – каждый процесс получает столько памяти, сколько запрашивает. Активные страницы переносятся в оперативную память для быстрого доступа, а неактивные страницы вытесняются во вторичную память по мере необходимости, чтобы освободить место. Когда отдельный процесс начинает потреблять большой объем памяти, он обычно занимает всё больше и больше оперативной памяти, вытесняя другие программы во вторичную память, что обычно значительно замедляет работу системы. Даже после завершения программы с утечкой может потребоваться некоторое время, чтобы другие программы вернулись в оперативную память и производительность восстановилась до нормальной. Когда вся память в системе исчерпана (независимо от наличия виртуальной памяти или только оперативной, как в случае встроенных систем), любая попытка выделить дополнительную память завершится неудачей. Обычно это приводит к завершению программы, пытающейся выделить память, или к генерации ошибки сегментации. Некоторые программы разработаны для восстановления после этой ситуации (возможно, за счет использования предварительно зарезервированной памяти). Первая программа, столкнувшаяся с нехваткой памяти, необязательно является программой, в которой произошла утечка памяти. Некоторые многозадачные операционные системы имеют специальные механизмы для обработки ситуации нехватки памяти, такие как случайное завершение процессов (что может повлиять на "невиновные" процессы) или завершение самого большого процесса в памяти (который, предположительно, является причиной проблемы). Некоторые операционные системы устанавливают лимит памяти для каждого процесса, чтобы предотвратить монополизацию всей памяти системы одной программой. Недостатком такой схемы является то, что операционную систему иногда необходимо перенастраивать для корректной работы программ, которым легитимно требуется большой объем памяти, например, программ для работы с графикой, видео или научных расчетов. Если утечка памяти происходит в ядре, то, скорее всего, откажет сама операционная система. Компьютеры без сложного управления памятью, такие как встроенные системы, также могут полностью выйти из строя из-за постоянной утечки памяти. Общедоступные системы, такие как веб-серверы или маршрутизаторы, подвержены атакам типа "отказ в обслуживании", если злоумышленник обнаружит последовательность операций, способную вызвать утечку. Такая последовательность называется эксплойтом. "Пилообразный" график использования памяти может указывать на утечку памяти в приложении, особенно если вертикальные падения совпадают с перезагрузками или перезапусками этого приложения. Однако следует проявлять осторожность, поскольку точки сборки мусора также могут вызывать подобную картину, указывая на нормальное использование кучи.

Другие потребители памяти

Обратите внимание, что постоянное увеличение использования памяти не обязательно свидетельствует об утечке памяти. Некоторые приложения хранят всё больше и больше информации в памяти (например, в виде кэша). Если кэш может вырасти настолько, что вызовет проблемы, это может быть ошибкой программирования или проектирования, но не утечкой памяти, поскольку информация остаётся в использовании. В других случаях программам может требоваться неоправданно большой объём памяти, потому что программист исходил из предположения о её достаточности для конкретной задачи; например, графический обработчик файлов может начать с чтения всего содержимого файла изображения и сохранения его в памяти, что становится невозможным при работе с очень большими изображениями, превышающими доступный объём памяти. Иными словами, утечка памяти является следствием конкретного типа ошибки программирования, и без доступа к исходному коду программы, наблюдатель, видящий симптомы, может лишь предполагать наличие утечки памяти. В ситуациях, когда нет доступа к исходному коду, лучше использовать такие термины, как "постоянно увеличивающееся использование памяти".