Слабые ссылки - насколько они полезны?

Итак, я размышлял над некоторыми идеями управления памятью в последнее время - в частности, я смотрел на реализацию менеджера памяти на основе подсчета ссылок. Конечно, всем известно, что циркулярные ссылки убивают наивный подсчет ссылок. Решение: слабые ссылки. Лично я ненавижу использовать слабые ссылки таким образом (есть другие более интуитивные способы борьбы с этим, через обнаружение циклов), но это заставило меня задуматься: где еще может быть полезно использовать слабую ссылку?

Я полагаю, что существует какая-то причина, по которой они существуют, особенно на языках с трассировкой коллекции мусора, которые не страдают от циклической ссылки (С# и Java - это те, с которыми я знаком, и Java даже имеет три вида слабых ссылок!). Однако, когда я пытался найти для них какие-то надежные прецеденты, у меня в значительной степени были такие идеи, как "Использовать их для реализации кешей" (я видел это несколько раз на SO). Мне это тоже не нравится, поскольку они полагаются на то, что GC трассировки, скорее всего, не будет собирать объект сразу же после того, как он больше не будет сильно ссылаться, за исключением ситуаций с низкой памятью. Эти типы случаев абсолютно недействительны со ссылкой на GC, поскольку объект уничтожается сразу же после того, как он больше не ссылается (за исключением, возможно, в случае циклов).

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

Ответ 1

Обработчики событий являются хорошим примером использования слабых ссылок. Объекту, который запускает события, требуется ссылка на объекты для вызова обработчиков событий, но вы, как правило, не хотите, чтобы контрольная ссылка производителя события не позволяла потребителям событий быть GC'd. Скорее, вы хотите, чтобы у продюсера событий была слабая ссылка, и тогда он будет отвечать за проверку наличия ссылочного объекта.

Ответ 2

Объект, на который ссылается WeakReference, будет доступен перед процессом gc.

Итак, если мы хотим иметь информацию об объекте до тех пор, пока он существует, мы можем использовать WeakReference. Например, Debugger и Optimizer часто должны иметь информацию об объекте, но они не хотят влиять на процесс GC.

Кстати, SoftReference отличается от WeakReference, потому что связанный объект будет собран только тогда, когда памяти недостаточно. Таким образом, SoftReference будет использоваться для создания глобального кэша.

Ответ 3

Я часто использую WeakReference в сочетании с ThreadLocal или InheritableThreadLocal. Если мы хотим, чтобы значение было доступно для ряда потоков, в то время как оно имеет смысл, но затем удаляло значение из этих потоков, мы фактически не можем освобождать память, потому что нет возможности вмешаться в значение ThreadLocal в потоке, отличном от текущего. Однако то, что вы можете сделать, это поместить значение в WeakReference в эти другие потоки (при создании значения - это предполагает, что один и тот же экземпляр разделяется между несколькими потоками, обратите внимание, что это имеет смысл только тогда, когда только подмножество потоков должно иметь доступ к этому значению или просто использовать статику) и сохранить твердую ссылку в другом ThreadLocal для некоторого рабочего потока, который будет удалять значение. Затем, когда значение перестает быть значимым, вы можете попросить рабочий поток удалить жесткую ссылку, которая вызывает немедленное выделение всех остальных потоков для сбора мусора (хотя они не могут быть немедленно собраны в мусор, поэтому стоит есть другой способ предотвратить доступ к значению).