stringtranslate.com

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

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

Связанное с этим понятие — «утечка пространства», когда программа потребляет слишком много памяти, но в конечном итоге освобождает ее. [3]

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

Последствия

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

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

Гораздо более серьезные утечки включают в себя:

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

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

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

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

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

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

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

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

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

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

Утечки памяти — распространенная ошибка в программировании, особенно при использовании языков , не имеющих встроенной автоматической сборки мусора , таких как 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 */ #include <stdlib.h> void f ( int n ) { int * array = calloc ( n , sizeof ( int )); do_some_work ( массив ); бесплатно ( массив ); }         
// Версия C++ #include <vector> void f ( int n ) { std :: vector <int> array ( n ) ; _ do_some_work ( массив ); }      

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

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

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

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

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

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

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

Dim A , B Set A = CreateObject ( «Some.Thing» ) Set B = CreateObject ( «Some.Thing» ) ' На этом этапе каждый из двух объектов имеет по одной ссылке,        Установите А. _ член = B Набор B. _ member = A ' Теперь у каждого из них есть две ссылки.      Установить A = Ничего ' Из этого еще можно выбраться...    Set B = Nothing ' И вот у вас утечка памяти!    Конец

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

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

Последствия

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

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

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

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

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

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

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

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

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

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

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

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

Следующая программа C++ намеренно приводит к утечке памяти, теряя указатель на выделенную память.

int main () { int * a = new int ( 5 ); а = нульптр ; /* Указатель в 'a' больше не существует и, следовательно, не может быть освобожден,  но память по-прежнему выделяется системой.  Если программа продолжит создавать такие указатели, не освобождая их,  она будет непрерывно потреблять память.  Следовательно, произойдет утечка. */ }           

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

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

  1. ^ Крокфорд, Дуглас. «Утечки памяти JScript». Архивировано из оригинала 7 декабря 2012 года . Проверено 20 июля 2022 г.
  2. ^ «Создание утечки памяти с помощью Java» . Переполнение стека . Проверено 14 июня 2013 г.
  3. ^ Митчелл, Нил. «Утечка пространства» . Проверено 27 мая 2017 г.
  4. ^ Рудафшани, Масуме и Пол А.С. Уорд. «LeakSpot: обнаружение и диагностика утечек памяти в приложениях JavaScript». Программное обеспечение, практика и опыт 47.1 (2017): 97–123. Веб.

Внешние ссылки