Утечка памяти

редактировать
Выполнение программы
Общие понятия
Типы кода
Стратегии компиляции
Заметное время выполнения
Известные компиляторы и инструментальные средства

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

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

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

СОДЕРЖАНИЕ
  • 1 Последствия
    • 1.1 Пример утечки памяти
  • 2 Проблемы программирования
  • 3 RAII
  • 4 Подсчет ссылок и циклические ссылки
  • 5 эффектов
  • 6 Другие потребители памяти
  • 7 Простой пример на C
  • 8 См. Также
  • 9 ссылки
  • 10 Внешние ссылки
Последствия

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

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

К гораздо более серьезным утечкам относятся:

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

Пример утечки памяти

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

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

Утечка памяти может произойти, если запрошенный номер этажа совпадает с этажом, на котором находится лифт; условие освобождения памяти будет пропущено. Каждый раз, когда возникает этот случай, происходит утечка памяти.

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

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

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

Утечку в приведенном выше примере можно исправить, вынеся операцию выпуска за пределы условного:

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, IBM Rational Purify, Valgrind, Parasoft Insure ++, Dr. Memory и memwatch - одни из наиболее популярных отладчиков памяти для программ C и C ++. «Консервативные» возможности сборки мусора могут быть добавлены к любому языку программирования, в котором он отсутствует как встроенная функция, а библиотеки для этого доступны для программ на C и C ++. Консервативный коллекционер находит и восстанавливает большую часть, но не все, недостижимые воспоминания.

Хотя диспетчер памяти может восстановить недоступную память, он не может освободить память, которая все еще доступна и, следовательно, потенциально полезна. Поэтому современные менеджеры памяти предоставляют программистам методы семантической маркировки памяти с различными уровнями полезности, которые соответствуют различным уровням достижимости. Диспетчер памяти не освобождает сильно достижимый объект. Объект является строго достижимым, если он доступен либо напрямую по сильной ссылке, либо косвенно по цепочке сильных ссылок. ( Сильная ссылка - это ссылка, которая, в отличие от слабой ссылки, предотвращает сбор объекта мусором.) Чтобы предотвратить это, разработчик несет ответственность за очистку ссылок после использования, обычно устанавливая для ссылки значение null, когда она больше не является необходимо и, если необходимо, отменив регистрацию всех прослушивателей событий, которые поддерживают строгие ссылки на объект.

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

RAII
Основная статья: Приобретение ресурсов - это инициализация

RAII, сокращение от Resource Acquisition Is Initialization, - это подход к проблеме, обычно используемый в C ++, D и Ada. Он включает в себя привязку объектов с заданной областью действия к полученным ресурсам и автоматическое освобождение ресурсов, когда объекты выходят за пределы области действия. В отличие от сборки мусора, RAII имеет то преимущество, что знает, когда объекты существуют, а когда нет. Сравните следующие примеры C и C ++:

/* C version */ #include lt;stdlib.hgt; void f(int n) { int* array = calloc(n, sizeof(int)); do_some_work(array); free(array); }
// C++ version #include lt;vectorgt; void f(int n) { std::vectorlt;intgt; 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 намеренно приводит к утечке памяти, теряя указатель на выделенную память. Можно сказать, что утечка происходит, как только указатель «a» выходит за пределы области видимости, т.е. когда функция function_which_allocates () возвращается без освобождения «a».

#include lt;stdlib.hgt; void function_which_allocates(void) { /* allocate an array of 45 floats */ float *a = malloc(sizeof(float) * 45); /* additional code making use of 'a' */ /* return to main, having forgotten to free the memory we malloc'd */ } int main(void) { function_which_allocates(); /* the pointer 'a' no longer exists, and therefore cannot be freed, but the memory is still allocated. a leak has occurred. */ }
Смотрите также
использованная литература
внешние ссылки
Последняя правка сделана 2024-01-02 06:55:36
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте