Утечка памяти
Эта статья включает список общих ссылок , но в ней отсутствуют достаточные соответствующие встроенные цитаты . ( сентябрь 2007 г. ) |
Выполнение программы |
---|
Общие понятия |
Типы кода |
Стратегии составления |
Известное время выполнения |
|
Известные компиляторы и наборы инструментов |
|
В информатике утечка памяти — это тип утечки ресурсов , который возникает, когда компьютерная программа неправильно управляет распределением памяти. [ 1 ] таким образом, та память , которая больше не нужна, не освобождается. Утечка памяти также может произойти, когда объект хранится в памяти, но к нему не может получить доступ работающий код (т. е. недоступная память ). [ 2 ] Утечка памяти имеет симптомы, аналогичные ряду других проблем, и обычно может быть диагностирована только программистом, имеющим доступ к исходному коду программы.
Связанное с этим понятие — «утечка пространства», когда программа потребляет слишком много памяти, но в конечном итоге освобождает ее. [ 3 ]
Поскольку утечки памяти могут исчерпать доступную системную память во время работы приложения, они часто являются причиной или фактором, способствующим старению программного обеспечения .
Последствия
[ редактировать ]Утечка памяти снижает производительность компьютера за счет уменьшения объема доступной памяти. Утечка памяти может привести к увеличению использования памяти, увеличению производительности во время выполнения и отрицательно повлиять на удобство работы пользователя. [ 4 ] В конце концов, в худшем случае, может быть выделено слишком много доступной памяти, и вся или часть системы или устройства перестанет работать правильно, приложение выйдет из строя или система сильно замедлится из-за сбоев .
Утечки памяти могут быть несерьезными и даже не обнаруживаемыми обычными средствами. В современных операционных системах обычная память, используемая приложением, освобождается при завершении приложения. Это означает, что утечка памяти в программе, которая работает только в течение короткого времени, может быть незамечена и редко является серьезной.
Гораздо более серьезные утечки включают в себя:
- когда программа работает в течение длительного времени и со временем потребляет дополнительную память, например, фоновые задачи на серверах и особенно во встроенных системах , которые могут работать в течение многих лет.
- где новая память часто выделяется для одноразовых задач, например, при рендеринге кадров компьютерной игры или анимационного видео.
- где программа может запрашивать память, например разделяемую память , которая не освобождается даже после завершения программы
- где память очень ограничена, например, во встроенной системе или портативном устройстве, или когда программе для начала требуется очень большой объем памяти, оставляя мало места для утечек.
- где происходит утечка в операционной системе или диспетчере памяти
- когда драйвер системного устройства вызывает утечку
- работающий в операционной системе, которая не освобождает память автоматически при завершении программы.
Пример утечки памяти
[ редактировать ]Следующий пример, написанный на псевдокоде , предназначен для того, чтобы показать, как может возникнуть утечка памяти и ее последствия, без каких-либо знаний программирования. Программа в данном случае является частью очень простого программного обеспечения, предназначенного для управления лифтом . Эта часть программы запускается всякий раз, когда кто-либо внутри лифта нажимает кнопку этажа.
When a button is pressed: Get some memory, which will be used to remember the floor number Put the floor number into the memory Are we already on the target floor? If so, we have nothing to do: finished Otherwise: Wait until the lift is idle Go to the required floor Release the memory we used to remember the floor number
Утечка памяти может произойти, если запрошенный номер этажа совпадает с этажом, на котором находится лифт; условие освобождения памяти будет пропущено. Каждый раз, когда происходит этот случай, происходит утечка большего количества памяти.
Подобные случаи обычно не имеют немедленных последствий. Люди не часто нажимают кнопку этажа, на котором они уже находятся, и в любом случае у лифта может быть достаточно свободной памяти, чтобы это могло произойти сотни или тысячи раз. Однако в конечном итоге у лифта закончится память. Это может занять месяцы или годы, поэтому его могут не обнаружить, несмотря на тщательное тестирование.
Последствия были бы неприятными; по крайней мере, лифт перестанет отвечать на запросы о переезде на другой этаж (например, когда предпринимается попытка вызвать лифт или когда кто-то находится внутри и нажимает кнопки этажа). Если другим частям программы потребуется память (например, часть, предназначенная для открытия и закрытия двери), то никто не сможет войти, а если кто-то окажется внутри, он окажется в ловушке (при условии, что двери не могут быть закрыты). открывается вручную).
Утечка памяти длится до перезагрузки системы. Например: если питание лифта было отключено или произошел сбой в подаче электроэнергии, программа остановилась бы. При повторном включении питания программа перезагружалась, и вся память снова была доступна, но медленный процесс утечки памяти возобновлялся вместе с программой, что в конечном итоге наносило ущерб правильной работе системы.
Утечку в приведенном выше примере можно исправить, вынеся операцию Release за пределы условного оператора:
When a button is pressed: Get some memory, which will be used to remember the floor number Put the floor number into the memory Are we already on the target floor? If not: Wait until the lift is idle Go to the required floor Release the memory we used to remember the floor number
Проблемы программирования
[ редактировать ]Утечки памяти — распространенная ошибка в программировании, особенно при использовании языков , не имеющих встроенной автоматической сборки мусора , таких как C и C++ . Обычно утечка памяти возникает из-за того, что динамически выделенная память стала недоступной . Распространенность ошибок утечки памяти привела к разработке ряда отладки инструментов для обнаружения недоступной памяти. BoundsChecker , Deleaker , Memory Validator, IBM Rational Purify , Valgrind , Parasoft Insure++ , Dr. Memory и memwatch — одни из наиболее популярных отладчиков памяти для программ C и C++. Возможности «консервативной» сборки мусора можно добавить в любой язык программирования, в котором она отсутствует, в качестве встроенной функции, а библиотеки для этого доступны для программ на C и C++. Консервативный коллекционер находит и возвращает большую часть, но не все, недоступные воспоминания.
Хотя диспетчер памяти может восстановить недоступную память, он не может освободить память, которая все еще доступна и, следовательно, потенциально все еще полезна. Поэтому современные менеджеры памяти предоставляют программистам методы семантической маркировки памяти с различными уровнями полезности, которые соответствуют различным уровням достижимости . Диспетчер памяти не освобождает объект, к которому имеется строгий доступ. Объект является строго достижимым, если он доступен либо напрямую по сильной ссылке , либо косвенно по цепочке сильных ссылок. ( Сильная ссылка — это ссылка, которая, в отличие от слабой ссылки , предотвращает сбор мусора для объекта.) Чтобы предотвратить это, разработчик несет ответственность за очистку ссылок после использования, обычно путем установки ссылки на ноль , как только она больше не является необходимо и, при необходимости, отменяя регистрацию всех прослушивателей событий , которые поддерживают строгие ссылки на объект.
В целом автоматическое управление памятью является более надежным и удобным для разработчиков, поскольку им не нужно реализовывать процедуры освобождения, беспокоиться о последовательности выполнения очистки или беспокоиться о том, ссылается ли объект на объект. Программисту легче узнать, когда ссылка больше не нужна, чем узнать, когда на объект больше не ссылаются. Однако автоматическое управление памятью может привести к снижению производительности и не устраняет все ошибки программирования, вызывающие утечки памяти.
РАИИ
[ редактировать ]Получение ресурсов — это инициализация (RAII) — это подход к проблеме, обычно используемый в C++ , D и Ada . Он включает в себя связывание объектов с заданной областью действия с приобретенными ресурсами и автоматическое освобождение ресурсов, как только объекты выходят за пределы области действия. В отличие от сборки мусора, RAII имеет то преимущество, что знает, когда объекты существуют, а когда нет. Сравните следующие примеры C и C++:
/* C version */
#include <stdlib.h>
void f(int n)
{
int* array = calloc(n, sizeof(int));
do_some_work(array);
free(array);
}
// C++ version
#include <vector>
void f(int n)
{
std::vector<int> array (n);
do_some_work(array);
}
Версия C, реализованная в примере, требует явного освобождения; массив динамически выделяется (из кучи в большинстве реализаций C) и продолжает существовать до тех пор, пока не будет явно освобожден.
Версия C++ не требует явного освобождения; это всегда будет происходить автоматически, как только объект array
выходит за рамки, в том числе в случае возникновения исключения. Это позволяет избежать некоторых накладных расходов, связанных со схемами сбора мусора . А поскольку деструкторы объектов могут освобождать ресурсы, отличные от памяти, RAII помогает предотвратить утечку входных и выходных ресурсов, доступ к которым осуществляется через дескриптор , с чем сборка мусора с маркировкой и очисткой не справляется корректно. К ним относятся открытые файлы, открытые окна, уведомления пользователей, объекты в библиотеке графических рисунков, примитивы синхронизации потоков, такие как критические секции, сетевые подключения и подключения к реестру Windows или другой базе данных.
Однако правильное использование RAII не всегда легко и имеет свои подводные камни. Например, если не быть осторожным, можно создать висячие указатели (или ссылки), возвращая данные по ссылке, только для того, чтобы эти данные были удалены, когда содержащий их объект выйдет за пределы области видимости.
D использует комбинацию RAII и сборки мусора, применяя автоматическое уничтожение, когда ясно, что к объекту нельзя получить доступ за пределами его исходной области действия, и сборку мусора в противном случае.
Подсчет ссылок и циклические ссылки
[ редактировать ]Более современные схемы сбора мусора часто основаны на понятии достижимости: если у вас нет полезной ссылки на рассматриваемую память, ее можно собрать. Другие схемы сборки мусора могут быть основаны на подсчете ссылок , когда объект отвечает за отслеживание количества ссылок, указывающих на него. Если число упадет до нуля, ожидается, что объект освободится и позволит освободить свою память. Недостаток этой модели заключается в том, что она не справляется с циклическими ссылками, и именно поэтому в настоящее время большинство программистов готовы принять на себя бремя более дорогостоящих маркировки и очистки систем типа .
Следующий код Visual Basic иллюстрирует каноническую утечку памяти при подсчете ссылок:
Dim A, B
Set A = CreateObject("Some.Thing")
Set B = CreateObject("Some.Thing")
' At this point, the two objects each have one reference,
Set A.member = B
Set B.member = A
' Now they each have two references.
Set A = Nothing ' You could still get out of it...
Set B = Nothing ' And now you've got a memory leak!
End
На практике этот тривиальный пример будет сразу замечен и исправлен. В большинстве реальных примеров цикл ссылок охватывает более двух объектов и его труднее обнаружить.
Хорошо известный пример такого рода утечки стал известен с появлением методов программирования AJAX в веб-браузерах в связи с проблемой истекшего прослушивателя . Код JavaScript , который связал элемент DOM с обработчиком событий и не смог удалить ссылку перед выходом, приводил к утечке памяти (веб-страницы AJAX поддерживают активность данного DOM намного дольше, чем традиционные веб-страницы, поэтому эта утечка была гораздо более очевидной). .
Эффекты
[ редактировать ]Если в программе есть утечка памяти и ее использование памяти постоянно увеличивается, немедленных симптомов обычно не возникает. Каждая физическая система имеет ограниченный объем памяти, и если утечку памяти не устранить (например, путем перезапуска программы, вызывающей утечку), это в конечном итоге приведет к проблемам.
Большинство современных потребительских операционных систем для настольных компьютеров имеют как основную память , которая физически размещена в микрочипах оперативной памяти, так и дополнительную память, например жесткий диск . Распределение памяти является динамическим — каждый процесс получает столько памяти, сколько запрашивает. Активные страницы переносятся в основную память для быстрого доступа; неактивные страницы перемещаются во вторичное хранилище, чтобы освободить место по мере необходимости. Когда один процесс начинает потреблять большой объем памяти, он обычно занимает все больше и больше основной памяти, вытесняя другие программы во вторичное хранилище, что обычно значительно замедляет производительность системы. Даже если программа, с которой произошла утечка, будет завершена, другим программам может потребоваться некоторое время, чтобы вернуться в основную память и производительность вернулась к нормальному состоянию.
Когда вся память в системе исчерпана (независимо от того, есть ли виртуальная память или только основная память, например, во встроенной системе), любая попытка выделить больше памяти потерпит неудачу. Обычно это приводит к тому, что программа, пытающаяся выделить память, завершает свою работу или генерирует ошибку сегментации . Некоторые программы предназначены для выхода из этой ситуации (возможно, используя заранее зарезервированную память). Первой программой, испытывающей нехватку памяти, может быть, а может и не быть программа, имеющая утечку памяти.
Некоторые многозадачные операционные системы имеют специальные механизмы для борьбы с нехваткой памяти, такие как случайное завершение процессов (что может повлиять на «невинные» процессы) или уничтожение самого большого процесса в памяти (который, предположительно, является причиной проблема). Некоторые операционные системы имеют ограничение памяти для каждого процесса, чтобы предотвратить использование какой-либо одной программой всей памяти в системе. Недостаток такой схемы заключается в том, что операционную систему иногда приходится переконфигурировать, чтобы обеспечить правильную работу программ, которым законно требуются большие объемы памяти, например программ, работающих с графикой, видео или научными расчетами.
Если утечка памяти происходит в ядре , скорее всего, выйдет из строя сама операционная система. Компьютеры без сложного управления памятью, такие как встроенные системы, также могут полностью выйти из строя из-за постоянной утечки памяти.
Общедоступные системы, такие как веб-серверы или маршрутизаторы, подвержены атакам типа «отказ в обслуживании», если злоумышленник обнаруживает последовательность операций, которая может вызвать утечку. Такая последовательность известна как эксплойт .
«Пилообразное» использование памяти может быть индикатором утечки памяти внутри приложения, особенно если вертикальные падения совпадают с перезагрузками или перезапусками этого приложения. Однако следует соблюдать осторожность, поскольку точки сбора мусора также могут вызывать такую ситуацию и показывать правильное использование кучи.
Другие потребители памяти
[ редактировать ]Обратите внимание, что постоянное увеличение использования памяти не обязательно является свидетельством утечки памяти. Некоторые приложения будут хранить постоянно увеличивающиеся объемы информации в памяти (например, в виде кэша ). Если размер кэша может стать настолько большим, что это может вызвать проблемы, это может быть ошибкой программирования или проектирования, но не является утечкой памяти, поскольку информация номинально остается в использовании. В других случаях программам может потребоваться неоправданно большой объем памяти, поскольку программист предполагал, что памяти всегда достаточно для конкретной задачи; например, процессор графических файлов может начать с чтения всего содержимого файла изображения и сохранения его в памяти, что неприемлемо, когда очень большое изображение превышает доступную память.
Другими словами, утечка памяти возникает из-за определенной ошибки программирования, и не имея доступа к программному коду, кто-то, увидев симптомы, может только догадываться, что может быть утечка памяти. Было бы лучше использовать такие термины, как «постоянно увеличивающееся использование памяти», там, где таких внутренних знаний не существует.
Простой пример на C++
[ редактировать ]Следующая программа C++ намеренно приводит к утечке памяти, теряя указатель на выделенную память.
int main() {
int* a = new int(5);
a = nullptr;
/* The pointer in the 'a' no longer exists, and therefore cannot be freed,
but the memory is still allocated by the system.
If the program continues to create such pointers without freeing them,
it will consume memory continuously.
Therefore, a leak would occur. */
}
См. также
[ редактировать ]- Переполнение буфера
- Управление памятью
- Отладчик памяти
- Plumbr — популярный инструмент обнаружения утечек памяти для приложений, работающих на виртуальной машине Java .
- nmon (сокращение от Nigel's Monitor) — популярный инструмент системного мониторинга для операционных систем AIX и Linux.
Ссылки
[ редактировать ]- ^ Крокфорд, Дуглас. «Утечки памяти JScript» . Архивировано из оригинала 7 декабря 2012 года . Проверено 20 июля 2022 г.
- ^ «Создание утечки памяти с помощью Java» . Переполнение стека . Проверено 14 июня 2013 г.
- ^ Митчелл, Нил. «Утечка пространства» . Проверено 27 мая 2017 г.
- ^ Рудафшани, Масуме и Пол А.С. Уорд. «LeakSpot: обнаружение и диагностика утечек памяти в приложениях JavaScript». Программное обеспечение, практика и опыт 47.1 (2017): 97–123. Веб.
Внешние ссылки
[ редактировать ]- Visual Leak Detector. Архивировано 15 декабря 2015 г. на Wayback Machine для Visual Studio, с открытым исходным кодом.
- Валгринд , открытый исходный код
- Deleaker для Visual Studio, проприетарный
- Валидатор памяти для Visual Studio, Delphi, Fortran, Visual Basic, проприетарный
- Обнаружение утечки памяти (с использованием поддержки отладки MFC)
- Статья « Обнаружение утечек памяти во встроенных системах ». Кэла Эриксона
- WonderLeak — высокопроизводительный профилировщик кучи и дескрипторов для Windows, запатентованный