Введение

Идиома программирования "Приобретение ресурсов - это инициализация" (RAII) — это идиома программирования, используемая в нескольких объектно-ориентированных, статически типизированных языках программирования для описания определенного поведения языка. В RAII владение ресурсом является инвариантом класса и связано со временем жизни объекта. Выделение ресурсов (или приобретение) происходит во время создания объекта (в частности, инициализации) конструктором, а освобождение ресурсов (выпуск) — во время уничтожения объекта (в частности, финализации) деструктором. Иными словами, приобретение ресурса должно быть успешным для успешной инициализации. Таким образом, ресурс гарантированно будет удерживаться между завершением инициализации и началом финализации (удержание ресурса является инвариантом класса) и будет удерживаться только пока объект существует. Следовательно, если нет утечек объектов, то не будет и утечек ресурсов. RAII наиболее тесно связано с C++, где оно возникло, но также используется в Ada, Vala и Rust. Эта техника была разработана для обеспечения безопасного управления ресурсами при исключениях в C++ в период 1984–1989 годов, в основном Бьярне Строуструпом и Эндрю Кенигом, а сам термин был придуман Строуструпом. Другие названия для этой идиомы включают Constructor Acquires, Destructor Releases (CADRe), а один из конкретных стилей использования называется Scope based Resource Management (SBRM). Последний термин относится к особому случаю автоматических переменных. RAII связывает ресурсы со временем жизни объекта, которое может не совпадать с началом и концом области видимости. (В частности, переменные, выделенные в динамической памяти, имеют время жизни, не связанное с какой-либо областью видимости.) Однако использование RAII для автоматических переменных (SBRM) является наиболее распространенным вариантом использования.

Справочное подсчет

Perl, Python (в реализации CPython) и PHP управляют временем жизни объектов с помощью подсчета ссылок, что делает возможным использование RAII. Объекты, на которые больше нет ссылок, немедленно уничтожаются или завершаются и освобождаются, поэтому деструктор или финализатор может освободить ресурс в этот момент. Однако использование деструкторов/финализаторов не всегда является идиоматичным в таких языках, и в Python оно специально не рекомендуется (в пользу контекстных менеджеров и финализаторов из пакета weakref). При этом время жизни объекта не обязательно привязано к какой-либо области видимости, и объекты могут быть уничтожены недетерминированно или вообще не уничтожены. Это может привести к случайной утечке ресурсов, которые должны были быть освобождены по окончании определенной области видимости. Объекты, хранящиеся в статической переменной (в частности, глобальной переменной), могут не быть завершены при завершении программы, поэтому их ресурсы не будут освобождены; CPython, например, не гарантирует завершение таких объектов. Более того, объекты с циклическими ссылками не будут собраны простым счетчиком ссылок и будут существовать неопределенно долго; даже если они будут собраны (с помощью более сложного сборщика мусора), время и порядок уничтожения будут недетерминированными. В CPython есть детектор циклов, который обнаруживает циклы и завершает объекты в цикле, однако до CPython 3.4 циклы не собирались, если какой-либо объект в цикле имел финализатор.