Введение

В программировании, ссылка, которая не защищает объект от сборки мусора. В компьютерном программировании слабая ссылка — это ссылка, которая не предотвращает сборку мусора объектом, на который она указывает, в отличие от сильной ссылки. Объект, на который ссылаются только слабые ссылки – то есть, "любая цепочка ссылок, ведущая к объекту, включает хотя бы одну слабую ссылку" – считается слабодоступным и может быть обработан как недоступный, а значит, может быть собран в любой момент времени. Некоторые языки со сборкой мусора поддерживают различные уровни слабых ссылок, такие как C#, Lua, Java, Lisp, OCaml, Perl, Python и PHP, начиная с версии 7.4.

Применение

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

Сбор мусора

Сбор мусора используется для очистки неиспользуемых объектов и, таким образом, снижает вероятность утечек памяти и повреждения данных. Существует два основных типа сборки мусора: трассировка и подсчёт ссылок. Схемы подсчёта ссылок регистрируют количество ссылок на данный объект и освобождают объект, когда счётчик ссылок становится равным нулю. Подсчёт ссылок не может освободить циклические (или кольцевые) ссылки, поскольку одновременно может быть освобождён только один объект. Группы взаимно ссылающихся объектов, которые не имеют прямых ссылок извне и недоступны, могут таким образом оставаться в памяти постоянно; если приложение непрерывно создаёт такие недоступные группы объектов, это приведёт к утечке памяти. Слабые ссылки (ссылки, которые не учитываются при подсчёте ссылок) могут быть использованы для решения проблемы циклических ссылок, если удастся избежать циклов, используя слабые ссылки для некоторых ссылок внутри группы. Распространённый пример различий между сильными и слабыми ссылками – древовидные структуры, такие как модель объектов документа (DOM), где ссылки от родителя к потомку сильные, а ссылки от потомка к родителю – слабые. Например, Apple Cocoa Framework рекомендует такой подход. Более того, даже если граф объектов не является деревом, древовидную структуру часто можно навязать понятием владения объектами, где отношения владения сильные и образуют дерево, а отношения, не связанные с владением, – слабые и не требуются для построения дерева. Этот подход распространён в C++ (до C++11), где сырые указатели используются как слабые ссылки. Однако этот подход имеет недостаток: он не позволяет определить, когда родительская ветвь была удалена. Начиная со стандарта C++11, решение было добавлено с использованием `shared_ptr` и `weak_ptr`, унаследованных из библиотеки Boost. Слабые ссылки также используются для минимизации количества ненужных объектов в памяти, позволяя программе указывать, какие объекты имеют меньший приоритет, ссылаясь на них только слабо.

Вариации

Некоторые языки имеют несколько уровней слабости ссылок. Например, в Java существуют, в порядке убывания слабости, мягкие, слабые и фантомные ссылки, определённые в пакете `java.lang.ref`. Каждый тип ссылки имеет связанное с ним понятие достижимости. Сборщик мусора (GC) использует тип достижимости объекта, чтобы определить, когда освободить объект. GC может безопасно освободить объект, который мягко достижим, но может решить этого не делать, если считает, что JVM может сэкономить память (например, если в JVM много неиспользованного пространства кучи). GC освободит объект, который слабо достижим, как только заметит его. В отличие от других типов ссылок, фантомную ссылку нельзя разыменовать. С другой стороны, фантомные ссылки предоставляют механизм для уведомления программы о том, что объект был освобождён (уведомление реализуется с использованием `ReferenceQueues`). В C# слабые ссылки различаются в зависимости от того, отслеживают ли они возрождение объекта или нет. Такого различия для сильных ссылок нет, поскольку объекты не завершаются, если на них есть какие-либо сильные ссылки. По умолчанию в C# слабые ссылки не отслеживают возрождение, то есть слабая ссылка не обновляется, если объект возрождён; такие ссылки называются короткими слабыми ссылками, а слабые ссылки, отслеживающие возрождение, – длинными слабыми ссылками. Некоторые языки, не использующие сборку мусора, такие как C++, предоставляют функциональность слабых/сильных ссылок как часть поддержки библиотек сборки мусора. Библиотека Boost C++ предоставляет сильные и слабые ссылки. Ошибочно использовать обычные указатели C++ в качестве слабых аналогов умных указателей, поскольку это лишает возможности обнаружить, когда счётчик сильных ссылок достиг 0 и объект был удалён. Более того, это не позволяет обнаружить, отслеживает ли другая сильная ссылка данный обычный указатель. Это создаёт возможность наличия двух (или более) умных указателей, отслеживающих один и тот же обычный указатель (что приводит к повреждению, как только счётчик ссылок одного из этих умных указателей достигнет 0 и объект будет удалён).

Примеры

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