В информатике, ручное управление памятью относится к использованию ручных инструкций программиста, чтобы выявить и освободить неиспользуемые объекты или мусор. Вплоть до середины 1990-х годов большинство языков программирования, используемых в промышленности, поддерживали ручное управление памятью, хотя сборка мусора существует с 1959 года, когда она была представлена вместе с Lisp. Однако сегодня языки со сборкой мусора, такие как Java, становятся все более популярными, а языки Objective-C и Swift предоставляют аналогичные функции за счет автоматического подсчета ссылок.. Основные вручную управляемые языки все еще широко используются сегодня являются C и C ++ - см распределение C динамической памяти.
Все языки программирования используют ручные методы, чтобы определить, когда выделить новый объект из бесплатного хранилища. C использует malloc
функцию; C ++ и Java используют new
оператор; и многие другие языки (например, Python) выделяют все объекты из бесплатного хранилища. Определение того, когда должен быть создан объект ( создание объекта ), обычно тривиально и не вызывает проблем, хотя такие методы, как пулы объектов, означают, что объект может быть создан перед немедленным использованием. Настоящая проблема - это разрушение объекта - определение того, когда объект больше не нужен (то есть мусор), и организация возврата его базового хранилища в свободное хранилище для повторного использования. При ручном выделении памяти это также указывается вручную программистом; через функции, такие как free()
C, или delete
оператор в C ++ - это контрастирует с автоматическим уничтожением объектов, содержащихся в автоматических переменных, в частности (нестатических) локальных переменных функций, которые уничтожаются в конце своей области видимости в C и C ++.
Например
Известно, что ручное управление памятью допускает несколько основных классов ошибок в программе при неправильном использовании, в частности, нарушения безопасности памяти или утечки памяти. Это значительный источник ошибок безопасности.
Известно, что языки, использующие исключительно сборку мусора, избегают двух последних классов дефектов. Утечки памяти все еще могут происходить (и ограниченные утечки часто возникают при поколенческой или консервативной сборке мусора), но обычно менее серьезны, чем утечки памяти в ручных системах.
У ручного управления памятью есть одно преимущество правильности, которое заключается в том, что оно позволяет автоматическое управление ресурсами с помощью парадигмы Resource Acquisition Is Initialization (RAII).
Это возникает, когда объекты владеют ограниченными системными ресурсами (такими как графические ресурсы, дескрипторы файлов или соединения с базой данных), от которых необходимо отказаться при уничтожении объекта - когда время жизни владения ресурсом должно быть привязано к времени жизни объекта. Языки с ручным управлением могут организовать это, получая ресурс во время инициализации объекта (в конструкторе) и освобождая его во время разрушения объекта (в деструкторе ), которое происходит в определенное время. Это называется инициализацией получения ресурсов.
Это также можно использовать с детерминированным подсчетом ссылок. В C ++ эта возможность используется для дальнейшего использования для автоматизации освобождения памяти в рамках ручной среды, использование shared_ptr
шаблона в стандартной библиотеке языка для управления памятью является распространенной парадигмой. shared_ptr
это не подходит для всех моделей использования объекта, однако.
Этот подход неприменим в большинстве языков со сборкой мусора - в частности, для отслеживания сборщиков мусора или более продвинутого подсчета ссылок - из-за того, что завершение не является детерминированным, а иногда вообще не происходит. То есть трудно определить (или определить), когда или может ли быть вызван метод финализатора ; это обычно известно как проблема финализатора. Java и другие языки с GC часто используют ручное управление ограниченными системными ресурсами, помимо памяти, с помощью шаблона удаления : ожидается dispose()
, что любой объект, который управляет ресурсами, реализует метод, который освобождает любые такие ресурсы и отмечает объект как неактивный. Ожидается, что программисты будут вызывать dispose()
вручную по мере необходимости, чтобы предотвратить «утечку» скудных графических ресурсов. В зависимости от finalize()
метода (как Java реализует финализаторы) освобождение графических ресурсов широко рассматривается как плохая практика программирования среди Java-программистов, и аналогичный __del__()
метод в Python не может использоваться для освобождения ресурсов. Для ресурсов стека (ресурсы, полученные и выпущенные в одном блоке кода) это можно автоматизировать с помощью различных языковых конструкций, таких как Python with
, C # using
или Java try
-with-resources.
Многие сторонники ручного управления памятью утверждают, что оно обеспечивает лучшую производительность по сравнению с автоматическими методами, такими как сборка мусора. Традиционно задержка была самым большим преимуществом, но теперь это не так. Ручное выделение часто имеет лучшую адресную ссылку.
Также известно, что ручное выделение больше подходит для систем, в которых память является ограниченным ресурсом из-за более быстрого восстановления. Системы памяти могут и часто "ломаются", когда размер рабочего набора программы приближается к размеру доступной памяти; неиспользуемые объекты в системе со сборкой мусора остаются в невосстановленном состоянии дольше, чем в системах с ручным управлением, поскольку они не восстанавливаются немедленно, что увеличивает эффективный размер рабочего набора.
Ручное управление имеет ряд задокументированных недостатков производительности:
delete
и подобные вызовы несут накладные расходы каждый раз, когда они выполняются, эти накладные расходы могут быть амортизированы за счет циклов сборки мусора. Это особенно верно для многопоточных приложений, в которых вызовы удаления должны быть синхронизированы.Задержка - это обсуждаемый вопрос, который со временем изменился: ранние сборщики мусора и простые реализации работали очень плохо по сравнению с ручным управлением памятью, но сложные современные сборщики мусора часто работают так же или лучше, чем ручное управление памятью.
Выделение вручную не страдает от длительных «пауз», которые возникают при простой остановке сборки мусора, хотя современные сборщики мусора имеют циклы сбора, которые часто незаметны.
Ручное управление памятью и сборка мусора страдают от потенциально неограниченного времени освобождения - ручное управление памятью, потому что освобождение одного объекта может потребовать освобождения его членов, рекурсивно членов его членов и т. Д., В то время как сборка мусора может иметь длинные циклы сборки. Это особенно актуально для систем реального времени, где неограниченные циклы сбора, как правило, недопустимы; Сборка мусора в реальном времени возможна путем приостановки сборщика мусора, в то время как для ручного управления памятью в реальном времени требуется избегать больших освобождений или вручную приостанавливать освобождение.