Гарантирует ли GC, что очищенные ссылки помещаются в очередь ReferenceQueue в топологическом порядке?
-
21-09-2019 - |
Вопрос
Скажем, есть два объекта, A
и B
, и есть указатель A.x --> B
, и мы создаем, скажем, WeakReference
для обоих A
и B
, с ассоциированным ReferenceQueue
.
Предположим, что оба A
и B
стать недостижимым.Интуитивно B
нельзя считать недостижимым раньше A
является.В таком случае получим ли мы каким-то образом гарантию того, что соответствующие ссылки будут поставлены в очередь в интуитивном (топологическом, когда циклов нет) порядке в ReferenceQueue
?Т.е.ref(A) перед ref(B).Я не знаю, а что, если GC пометит кучу объектов как недоступные, а затем поставит их в очередь в произвольном порядке?
я просматривал Финализатор.java гуавы, увидев этот фрагмент:
private void cleanUp(Reference<?> reference) throws ShutDown {
...
if (reference == frqReference) {
/*
* The client no longer has a reference to the
* FinalizableReferenceQueue. We can stop.
*/
throw new ShutDown();
}
frqReference
является PhantomReference на используемый ReferenceQueue
, поэтому, если это GC, никакие Finalizable{Weak, Soft, Phantom}References не могут быть активными, поскольку они ссылаются на очередь.Таким образом, они должны быть GC'ed, прежде чем сама очередь может быть GC'ed - но тем не менее, получаем ли мы гарантию, что эти ссылки будут поставлены в очередь в очередь? ReferenceQueue
в том порядке, в котором они «собирают мусор» (как если бы они собирали мусор один за другим)?В коде подразумевается, что есть какая-то гарантия, иначе необработанные ссылки теоретически могут остаться в очереди.
Спасибо
Решение
Гарантии на заказ нет.В случае Finalizer.java поток можно закрыть до того, как будут обработаны все ссылки.См. документацию FinalizableReferenceQueue:
Сохраняйте строгую ссылку на этот объект до тех пор, пока все связанные
- референты были окончательно определены.Если этот объект представляет собой мусор, собранный ранее,
- поддерживающий поток не будет вызывать {@code FinalizeReferent()} в
- оставшиеся ссылки.
Это намеренное поведение.Например, мы используем FRQ для очистки записей карты, когда удаляются ссылки на ключи и/или значения.Если у пользователя больше нет ссылки на карту и, в свою очередь, больше нет ссылки на FRQ, нет смысла обрабатывать эти ссылки.
Другие советы
Я почти уверен, что ответ — нет.
Спецификация JVM говорит следующее о методах финализатора:
Виртуальная машина Java не накладывает никакого порядка на вызовы метода Finalize.Финализаторы могут вызываться в любом порядке или даже одновременно.(Спецификация JVM 2.17.7)
Из этого я делаю вывод, что нет никаких гарантий, что ссылки располагаются в очереди в топологическом порядке.
Я думаю, такой гарантии нет.Сам сборщик мусора не имеет полного и непосредственного представления об оперативной памяти (не может, поскольку сборщик мусора работает на процессоре, который может просматривать только несколько байтов за раз).В вашем примере, предполагая базовый сборщик мусора «пометка и очистка», есть вероятность, что A и B будут объявлены недостижимыми на одной и той же фазе пометки и будут сведены вместе в произвольном порядке.Поддержание топологического порядка, вероятно, будет дорогостоящим.
Что касается Finalizer
, похоже, что он предназначен для использования через FinalizableReferenceQueue
только экземпляр, который творит некоторую магию, связанную с загрузчиком классов.Тем Finalizer
использует свои собственные средства для обнаружения, когда FinalizableReferenceQueue
от которого оно функционально зависит, само становится недостижимым;это момент, когда поток, который работает Finalizer
знает, что он должен выйти.Насколько я понимаю, если приложение позволяет GC вернуть FRQ, то поток финализатора завершится, и любые ссылки, поставленные в очередь «после» ссылки FRQ, не будут обработаны.Это зависит от топологического порядка или его отсутствия, но я не могу решить, проблема это или нет.Я думаю, что приложение не должно отказываться от своего FRQ, пока важна обработка возвращенных объектов, на которые ссылаются.