Как узнать, что память, которую занимал объект, освобождена?
В некоторых языках сделать это очень просто: вызвал delete, и можешь быть уверен – памяти стало больше. В Java всё немного сложнее.
С одной стороны, отлично, что нам не нужно писать delete «ручками». GC всё сделает за тебя. Потерял ссылку на объект и ничего страшного! Память будет освобождена и без ссылки. Утечки не будет. С другой стороны, очень хочется иногда написать delete, но нет такой возможности.
И, в самом деле, как узнать, что объект удалён?
Можно подписаться на события GC. Но они не про конкретный объект, а про событие для всех объектов. Можно переопределить finalize() (у всех классов он есть) и обработать событие в нём.
Но если таких объектов будет много, это может сильно снизить скорость работы GC. А медленный GC – это большие паузы в работе всего приложения. К счастью в Java есть отличный способ узнать о том, что объект удалён.
Phantom reference
Фантомная ссылка — объект класса java.lang.ref.PhantomReference. Объект, на который существуют только фантомные ссылки, доступен сборщику мусора. Фантомная ссылка не даёт пользователю возможности «спасти» объект, создав на него сильную ссылку: метод get на ней всегда возвращает null.
Зато с помощью фантомной ссылки можно определить, что объект собирается быть удалён. В силу этих особенностей фантомная ссылка бесполезна без объекта ReferenceQueue, поэтому для неё определён единственный конструктор:
ReferenceQueue<Wiki> queue = new ReferenceQueue<Wiki>(); //создание очереди PhantomReference<Wiki> ref = new PhantomReference<Wiki>(new Wiki("cyclowiki.org"), queue);
Основное предназначение фантомных ссылок — замена методу finalize, имеющему большое количество недостатков. С их помощью можно реализовать логику подготовки объекта к удалению, например освобождение захваченных им ресурсов, очистка сессии, сохранение состояния или логгирование.
Фантомная ссылка, указывающая на удаляемый объект, попадает в ReferenceQueue, откуда она может быть извлечена. Поскольку базовая фантомная ссылка не предоставляет пользователю возможности определить, на какой именно объект она ссылается, стоит реализовывать собственного потомка класса PhantomReference, хранящего идентификатор объекта (не ссылку на сам объект, ибо это будет сильная ссылка — объект перестанет быть доступен сборщику мусора).
В таком случае после обращения к идентификатору можно однозначно определить, какой объект был удалён, и выполнить все необходимые операции.
Проще говоря
Phantom reference – тоже ссылка на объект, как и нормальная ссылка. Но у нее всё по-своему. Получить объект по ней нельзя. Но можно найти её в специальном пуле ссылок для удалённых объектов. Создали ссылку, подождали GC, проверили в пуле. Если она там есть, значит объект был удалён.
А какие еще типы ссылок есть в Java? И зачем они нужны?
Знаете? Напишите в комментариях!