вторник, 8 декабря 2015 г.

Кое-что о ссылках в java

Сегодня поговорим о том, какие бывают ссылки в языке java.
Их всего четыре вида: Strong, Weak, Phantom и Soft.
Чтобы воспользоваться ими, за исключением первой, нужно использовать соответствующие классы из пакета java.lang.refs
Чем же они различаются и какими суперспособностями обладает каждая из них? Различия в основном связаны с поведением сборщика мусора, в тот момент когда он их обрабатывает.

Strong Reference - это самая обычная ссылка. Например, когда мы создаем объект и объявляем ссылку на него, именно она в данном случае и есть.

Сборщик мусора не удалит этот объект до тех пор, пока у объекта есть хотя бы одна достижимая strong ссылка.
С этим надеюсь все понятно.

Следующая ссылка - это weak reference. Данный тип ссылки игнорируется сборщиком мусора. Объект может удалиться, даже если на него ссылаются несколько этих самых weak reference. Обычно, такие ссылки, наряду с soft reference, используются при построении кешей, для избежания утечек памяти.

Soft reference очень схожа с предыдущей ссылкой, за исключением одного момента. Сборщик мусора вправе удалить объект, на который ссылаются эти ссылки, только если возникла OutOfMemoryError, т.е. когда JVM явно не хватает памяти.
Создаются они довольно просто:

Чтобы описать фантомные ссылки, нужно рассказать поподробнее про то, как сборщик мусора освобождает память. Он обходит объекты по ссылкам из определенных точек - GC roots (локальные переменные и параметры метода, действующие потоки, JNI ссылки, классы загруженные системным загрузчиком классов) и проверяет их доступность. Строится некоторый граф. Таким образом, если объект не попадает в этот граф, то он считается не доступным. С точки зрения сборщика мусора его можно безболезненно удалить. Если у класса переопределен метод finalize(), то объект удаляется в два этапа. На первом шаге, происходит выполнение этого метода. Затем, если сборщик мусора, снова сочтет данный объект бесполезным и попытается удалить, он проверит, был ли вызван метод finalize(), если да, то объект удаляется. Таким образом в методе finalize() можно случайно или нарочно "воскресить" объект - передав ссылку на него другому объекту.

Теперь про phantom reference. Довольно странный тип ссылок, хотя бы потому что метод get() всегда возвращает null :) Он используется для выполнения некоторой логики, уже после того когда отработал метод finalize() - т.е. для создания трехфазного удаления. Взглянем на то, как создается фантомная ссылка:
В конструктор, помимо самого объекта, передается еще и очередь. Для чего она нужна? А вот для чего, сборщик мусора, обнаружив объект, который имеет:

  1. weak reference или
  2. soft reference при возникновении исключения OutOfMemoryError
и как минимум, одну ссылку phantom reference, вызывает метод, если он переопределен, finalize(), а затем во втором проходе добавляет эту ссылку в очередь, и не удаляет объект, до тех пор, пока ссылка находится в этой очереди.
Таким образом, можно создать фоновый поток, который бы опрашивал эту очередь, доставал бы от туда ссылки и выполнял какую нибудь логику. Но нам не нужно ничего писать, в пакете java.misc уже есть класс Cleaner, который берет эту работу на себя. Примерно так это выглядит:

Хочется подытожить и сказать, что многие специалисты не рекомендуют использовать метод finalize() и писать там критичные операции, использование двойной финализации без очень резких на то причин, крайне нежелательно и неоправданно.