#java #garbage-collection #weak-references
#java #сборка мусора #слабые ссылки
Вопрос:
В каком состоянии находится объект, когда вы вручную ставите ссылку в очередь?
this.s = "foo";
WeakReference<String> wr = new WeakReference<String>(this.s);
wr.enqueue();
Во всей документации, которую я нашел, говорится о сборщике мусора, ставящем объект в очередь, а не о том, что происходит, когда вы делаете это вручную.
И есть ли какая-либо ситуация, когда это имело бы смысл? Что означает, что объект должен быть помещен в очередь, но при этом иметь доступные ссылки (сильные, слабые, фантомные)?
Редактировать: дополнительный бонусный вопрос: будет ли объект снова помещен в очередь, когда он станет недоступен в отдаленном будущем?
Комментарии:
1. Как вы думаете, какую возможную выгоду вы получаете от ручной постановки ссылки в очередь? Очередь ссылок существует для уведомления вас о том, что что-то произошло, а не для запуска поведения.
2. @kdgregory — это именно тот вопрос, который я задаю. Зачем вам это делать, и если вы делаете, что это значит? Есть способ сделать это вручную, так что, предположительно, это не совсем бесполезно.
3. JDK достаточно большой и достаточно старый, поэтому в нем много битов, которые не имеют хорошего объяснения. Возможно, в то время кому-то это показалось хорошей идеей. Возможно, она была необходима для какой-то ранней версии JDK, а затем не могла быть удалена для обеспечения обратной совместимости. Хотя я приветствую ваше желание учиться, я думаю, что обучение ради обучения довольно бессмысленно.
4. @kdgregory, я думаю, здесь ты ошибаешься. Изучение того, как все работает, почти всегда полезно (и изучение само по себе хорошо, точка). И JDK не такой уж старый. Множество людей, работавших над ней в 1996 году, все еще работают профессионально, и существует огромное количество информации о процессе ее разработки.
Ответ №1:
Сильная ссылка останется, поэтому объект не подходит для сбора. Как указывает JVestry, если вы создаете слабую ссылку с помощью очереди, то wr.enqueue() возвращает true, как и ожидалось.
Потенциальным применением для этого было бы, чтобы ваш обработчик мог работать с объектом, подлежащим либо ожидающей сборки мусора, либо какому-либо другому изменению состояния системы, когда вы хотели бы выполнить то же действие, что и при сборе объекта, например, возможно, вы сохраняете мягкую ссылку на что-то, что содержит системные ресурсы, чтобы система могла справиться с ситуацией нехватки памяти, в то время как вы все еще можете управлять закрытием ресурсов самостоятельно, если закончите нормально.
Что касается второго вопроса, ссылка ставится в очередь только один раз, независимо от того, делаете ли вы это с помощью enqueue() или gc выполняет это как часть коллекции.
РЕДАКТИРОВАТЬ Важно помнить, что связь между referencequeue связана со ссылкой, в данном случае wr, а не с референтом, в данном случае s. Вы могли бы иметь несколько слабых ссылок на один и тот же объект и получать несколько очередей (вроде), по одной для каждой слабой ссылки. Но в очереди находится слабая ссылка, а не референт, хотя она все равно должна быть доступна из слабой ссылки. Более подробную информацию смотрите в javadoc на java.lang.ref в разделе уведомлений.
Комментарии:
1. Я не хочу быть слишком анальным в этом вопросе, но вы никогда не знаете, сколько других ссылок существует на объект со слабыми ссылками в других частях кода, особенно если это ресурс. Если вы можете отбросить такой объект, у вас должен быть метод, чтобы пометить его таким образом и затем выполнить освобождение ресурса. Почему? Потому что принуждение других ссылок проверять помещенную в очередь слабую ссылку является грубым нарушением OO-дизайна. Состояние объекта больше не зависит от самого объекта. Это также открытая дверь для сумасшедших ошибок во время выполнения…
2. @JVerstry — Если я управляю доступом к ресурсу с мягкой ссылкой, то да, мне действительно нужно проверить результат reference.get(), поскольку ссылка, возможно, была собрана. Это не является нарушением каких-либо принципов проектирования. Если ресурс является общим, то, независимо от использования программной ссылки или постановки в очередь для закрытия ресурса, доступ к ресурсу не может безопасно принимать состояние, и весь доступ должен проходить некоторую базовую проверку и обработку ошибок. Использование enqueue для закрытия ресурса — это просто повторное использование механизма, который я должен иметь для обработки коллекции ссылки.
3. «в очереди находится слабая ссылка, а не референт» — это то, что должно было быть очевидным для меня, но по какой-то причине не было.
Ответ №2:
Есть хорошая статья о типах ссылок в Java: http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html
В нескольких словах: вы можете сделать что-то вроде этого:
public class Main {
public static void main(String[] args) throws InterruptedException{
String weakString = "Help me";
ReferenceQueue<String> refQ = new ReferenceQueue<String>();
WeakReference<String> weakRef = new WeakReference<String>(weakString, refQ);
//there is no more "hard reference to the string"
weakString = null;
//there is no object in ReferenceQueue
System.out.println(refQ.poll());
//put the object in the ReferenceQueue
weakRef.enqueue();
while (true) {
//There will be returned "Help me" until the garbage collector will "kill"
//"weak" string. This will be done after some time because there are no
//more strong references to the string. After that will be returned a null
//object.
System.out.println(weakRef.get());
//only one time there will be returned a refenrece to the String object
System.out.println(refQ.poll());
Thread.sleep(1000);
}
}
}
Это предназначено для работы с некоторым видом кэша.
Таким образом, вы можете быть уверены, что в памяти не будет неиспользуемого объекта в течение длительного времени.
ReferenceQueue можно использовать для выполнения какой-либо очистки после вызова weakRef.enqueue();
. Например, закрытие файлов или потоков, которые использовались для работы с данными.
Комментарии:
1. Я видел эту статью, но, похоже, в ней что-то не так: «Единственное применение такой ссылки — отслеживать, когда она попадает в очередь ReferenceQueue, поскольку в этот момент вы знаете, что объект, на который она указывала, мертв». Если вы можете поместить что-то в очередь вручную, пока у него все еще есть доступные ссылки, то вы не можете считать, что оно мертво только потому, что оно было помещено в очередь.
2. Имхо, ReferenceQueue дает возможность вручную очистить некоторые данные, связанные с собранным объектом. Итак, если у вас есть метод, который периодически проверяет очередь и очищает объект, метод «enqueue» предлагает вам возможность очистить объект до того, как он будет собран GC.
3. Но вы можете просто очистить ее — нет необходимости ставить в очередь.
4. Я имею в виду — есть другой поток, который отвечает за очистку слабых объектов. В другом потоке / части вашего приложения вы можете поместить бесполезный объект в очередь и таким образом дать сигнал потоку «cleaner» выполнить свою работу.
5. @StKiller Ваш пример не работает. Литеральная строка никогда не получает GC’d .. Распределите Strring следующим образом: String weakString = новая строка(«Помогите мне!»);
Ответ №3:
В вашем случае wr.enqueue() вернет false, потому что вы не указали очередь при создании WeakReference. Если вы укажете ReferenceQueue при создании WeakReference, wr.enqueue() вернет true.
Ставить слабую ссылку в очередь вручную не имеет смысла. Это все равно, что записать, что слабая ссылка была собрана мусором, хотя этого не было: ‘s’ по-прежнему является сильной ссылкой на «foo».
Комментарии:
1. Для чего используется метод enqueue()? Если это никогда не может иметь смысла, зачем оно там? (В документе очень четко указано, что сам сборщик мусора не использует enqueue(), так что дело не в этом.)
2. Скорее всего, это необходимо для целей компиляции и генерации байтового кода. Вам нужна ручка в байт-коде для вызова соответствующего байт-кода enqueue(), но Java не будет вызывать метод, потому что он может быть переопределен. Если бы это произошло, пользовательский код мог бы вызвать настоящий беспорядок и нарушить сборку мусора…
Ответ №4:
есть ли какая-либо ситуация, когда это имело бы смысл?
ReferenceQueues иногда используются для управления ресурсами, в таком случае имело бы смысл самостоятельно вызывать enqueue() для освобождения ресурсов до запуска GC, поскольку некоторые ресурсы довольно ограничены (это можно было бы сделать в методе dispose() с использованием самой слабой ссылки в качестве меры безопасности).
добавлено: Это может быть полезно, если очистка требует времени, поскольку referencequeues могут быть прочитаны в их собственном потоке, это не приведет к зависанию вашего основного потока.
Что означает, что объект должен быть помещен в очередь, но при этом иметь доступные ссылки (сильные, слабые, фантомные)?
В документации указано, что она вызывается после того, как ссылке на объект присвоено значение null, поэтому функция enqueue() не может напрямую влиять на состояние объекта В текущей документации указано, что вызов enqueue не используется gc, он предназначен для постановки ссылки в очередь до того, как gc это сделает. Этот вызов не повлияет на объект, на который ссылается, однако код очистки, считывающий referencequeue, может утилизировать ресурсы, используемые этим объектом.
Изменения: сейчас проверил документы 1.6, поэтому исправил ошибку о gc с использованием enqueue (в старых документах на это не указывалось, однако имеет смысл ограничить доступ gc к пользовательскому коду)
Комментарии:
1. «имело бы смысл самостоятельно вызывать enqueue() для освобождения ресурсов перед запуском GC» -> Вовсе нет, ресурс не обязательно будет освобожден простым вызовом enqueue(). Вы бы только обманывали себя. Более того, другая слабая ссылка на тот же объект может сама не быть помещена в очередь.
2. @JVerstry это, естественно, будет зависеть от кода очистки, который обрабатывает очередь. Я имел в виду, что это может быть полезно <b>, когда</b> ссылка имеет referencequeue, выполняющий обработку ресурсов, и в этом случае постановки в очередь одной ссылки было бы достаточно для кода очистки, считывающего очередь.
3. В этом случае вам гораздо лучше выполнить очистку «сейчас», а не делегировать очистку чему-либо, отслеживающему очередь.
4. @JVerstry очередь ссылок может быть прочитана в ее собственном потоке, выполняющем «очистку сейчас», когда с очисткой связаны операции, зависящие от потока, вызов enqueue был бы самым простым способом распоряжения ресурсами вручную.