Утечка памяти - Memory leak

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

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

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

Последствия

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

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

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

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

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

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

Когда нажимается кнопка: запомните, что будет использоваться для запоминания номера этажа. Поместите номер этажа в память. Мы уже на целевом этаже? Если это так, нам нечего делать: готово В противном случае: подождите, пока лифт не остановится Перейти на требуемый этаж Освободите память, которую мы использовали для запоминания номера этажа

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

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

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

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

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

При нажатии кнопки: запомните, что будет использоваться для запоминания номера этажа. Поместите номер этажа в память. Мы уже на целевом этаже? Если нет: подождите, пока лифт не будет работать. Перейти на нужный этаж. Освободите память, которую мы использовали для запоминания номера этажа.

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

Утечки памяти - частая ошибка в программировании, особенно при использовании языки которые не имеют встроенного автоматического вывоз мусора, Такие как C и C ++. Обычно утечка памяти происходит из-за динамически распределяется память стала недоступен. Распространенность утечки памяти ошибки привел к развитию ряда отладка инструменты для обнаружения недоступной памяти. BoundsChecker, Deleaker, IBM Rational Purify, Валгринд, Parasoft Страхование ++, Доктор Память и memwatch некоторые из наиболее популярных отладчики памяти для программ на C и C ++. «Консервативные» возможности сборки мусора могут быть добавлены к любому языку программирования, в котором он отсутствует, как встроенная функция, а библиотеки для этого доступны для программ C и C ++. Консервативный коллекционер находит и восстанавливает большую часть, но не все, недостижимые воспоминания.

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

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

RAII

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

/ * Версия C * /#включают <stdlib.h>пустота ж(int п){  int* множество = каллок(п, размер(int));  поработай немного(множество);  свободный(множество);}
// Версия C ++#включают <vector>пустота ж(int п){  стандартное::вектор<int> множество (п);  поработай немного(множество);}

Версия C, реализованная в примере, требует явного освобождения; массив динамически распределяется (из кучи в большинстве реализаций C) и продолжает существовать, пока явно не освобожден.

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

Однако правильно использовать RAII не всегда просто и есть свои подводные камни. Например, если неосторожно, можно создать висячие указатели (или ссылки), возвращая данные по ссылке, только чтобы эти данные были удалены, когда содержащийся в них объект выходит за пределы области видимости.

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

Подсчет ссылок и циклические ссылки

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

Следующее Visual Basic код иллюстрирует каноническую утечку памяти с подсчетом ссылок:

Тусклый А, BНабор А = CreateObject("Что нибудь")Набор B = CreateObject("Что нибудь")'На этом этапе каждый из двух объектов имеет одну ссылку,Набор А.член = BНабор B.член = А«Теперь у каждого из них есть по две ссылки.Набор А = Ничего   «Еще можно было выйти из этого ...Набор B = Ничего   - А теперь у вас утечка памяти!Конец

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

Хорошо известный пример такого рода утечек стал известен с ростом AJAX методы программирования в веб-браузеры в проблема с пропущенным слушателем. JavaScript код, связанный с ДОМ элемент с обработчиком событий и не смог удалить ссылку перед выходом, приведет к утечке памяти (веб-страницы AJAX сохраняют данную DOM в действии намного дольше, чем традиционные веб-страницы, поэтому эта утечка была гораздо более очевидной).

Последствия

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

Самый современный потребительский компьютер операционные системы есть оба основная память который физически размещен в микрочипах RAM, и вторичное хранилище например, жесткий диск. Распределение памяти является динамическим - каждый процесс получает столько памяти, сколько запрашивает. Активный страницы переносятся в основную память для быстрого доступа; неактивные страницы выталкиваются во вторичное хранилище, чтобы освободить место по мере необходимости. Когда один процесс начинает потреблять большой объем памяти, он обычно занимает все больше и больше основной памяти, выталкивая другие программы во вторичное хранилище, что обычно значительно снижает производительность системы. Даже если программа с утечкой завершена, другим программам может потребоваться некоторое время, чтобы переключиться обратно в основную память и восстановить нормальную производительность.

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

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

"Пилообразная" модель использования памяти: внезапное падение объема используемой памяти является вероятным признаком утечки памяти.

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

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

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

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

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

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

Простой пример на C

Следующее C функция намеренно приводит к утечке памяти, теряя указатель на выделенную память. Можно сказать, что утечка происходит, как только указатель 'a' выходит за пределы области видимости, то есть когда function_which_allocates () возвращается без освобождения 'a'.

#включают <stdlib.h>пустота function_which_allocates(пустота) {    / * выделяем массив из 45 чисел с плавающей запятой * /    плавать *а = маллок(размер(плавать) * 45);    / * дополнительный код, использующий 'a' * /    / * возвращаемся в main, забыв освободить память, которую мы заблокировали * /}int главный(пустота) {    function_which_allocates();    / * указатель 'a' больше не существует и поэтому не может быть освобожден,     но память по-прежнему выделена. произошла утечка. * /}

Смотрите также

Рекомендации

  1. ^ Крокфорд, Дуглас. «Утечки памяти JScript». Архивировано из оригинал 7 декабря 2012 г.. Получено 6 ноября 2012.
  2. ^ «Создание утечки памяти с помощью Java». Переполнение стека. Получено 2013-06-14.
  3. ^ Митчелл, Нил. "Текущее пространство". Получено 27 мая 2017.

внешняя ссылка