24 января 2012 г.

Ссылки в Java

Все знают что такое жесткая (strong) ссылка. Мы сталкиваемся с ними ежедневно. Например, этот код:
StringBuffer buffer = new StringBuffer();
создаст экземпляр класса StringBuffer и поместит жесткую ссылку на этот экземпляр в переменную buffer. Что же заставляет нас называть такую ссылку жесткой? И какие они еще бывают? Ответы - под катом.


Если объект доступен через цепочку ссылок (как например объект, на который ссылается переменная buffer), то такая ссылка называется жесткой и сборщик мусора не станет уничтожать такой объект. Чаще всего это именно то, что нам нужно.

Иногда мы работаем с классами, которые не можем расширить. К примеру, класс был помечен как final или мы вообще работаем с интерфейсом, который нам возвращает какая-то фабрика и мы даже не знаем конкретную реализацию. Допустим, у вас есть класс, который является представлением некой "вещи" - Thing. Этот класс вы не можете изменить и/или расширить. Что произойдет, если вам нужно получить немного больше информации об объекте, чем он предоставляет? Допустим, мы хотим помимо информации, хранящейся в объекте, хранить еще какой-либо серийный номер "вещи", но мы не можем расширить этот класс. На помощь приходит HashMap:
serialNumberMap.put(thing, thingSerialNumber);
Вроде бы всё хорошо, но жесткая ссылка может создать проблемы. Мы должны знать (со 100% уверенностью) когда приложению необходимо знать серийный номер "вещи" чтобы удалить экземпляр из коллекции и позволить сборщику мусора очистить память. Иначе мы получим утечку памяти или обращение к несуществующему элементу коллекции.

Другая проблема с жесткими ссылками - это кэширование. Допустим вы работаете над приложением, которое должно обрабатывать множество больших изображений. Хорошей идеей было бы закэшировать эти изображения, т.к. постоянная их загрузка может отрицательно сказатсья на производительности, к тому же кэширование позволит нам избежать ситуации, когда одно и то же изображение загружается в память дважды. Кэш должен всегда хранить ссылку на изображение. Скорее всего через какой-то промежуток времени закэшированые изображения станут не актуальны. Т.к. мы используем жесткие ссылки сборщик мусора не сможет очистить память, которую занимает закэшированное изображение, до тех пор, пока мы не перенаправим ссылку на изображения на null. Как и в случае с классом Thing, мы приходим к ручному управлению памятью, а значит и потенциально к ошибкам.

Слабые ссылки (WeakReference)

Слабая ссылка - это ссылка, которая недостаточно сильна чтобы объект не собирался сборщиком мусора. Создаются так:
WeakReference<Thing> weakThing = new WeakReference<Thing>(thing);
теперь вы можете использовать weakThing.get() чтобы получить экземпляр Thing. Т.к. слабая ссылка недостаточно сильна чтобы предотвратить сборку мусора когда-нибудь при вызове weakThing.get() вы получите null.

Чтобы решить задачу с серийными номерами вы можете воспользоваться классом WeakHashMap, который работает так же, как и HashMap, кроме того, что его ключи (не значения!) ссылаются через слабые ссылки. Если ключ WeakHashMap становится мусором - элемент коллекции удаляется автоматически.

Мягкие ссылки (SoftReference)

По своей природе данный вид ссылок очень похож на WeakReference, с одним очень существенным отличием: объекты по ссылкам уничтожаются в том случае, когда память вашей программы заполнена и появляется вероятность получить OutOfMemoryError. Именно этот вид ссылок рекомендуется использовать для кэша. Создаются они так:
SoftReference<Thing> thing = new SoftReference<Thing>(new Thing());

Фантомные ссылки (PhantomReference)

Фантомные ссылки достаточно сильно отличаются от слабых или мягких ссылок. Связь с объектами в этих ссылках такая слабая, что вы даже не сможете получить эти объекты - get() метод всегда будет возвращать null. Единственная область применения этих ссылок в отслеживании момента, когда ссылка помещается в очередь ReferenceQueue. Когда это происходит вы можете быть уверенным, что объект, на которой указывает ссылка, мертв. Создаются они так:
PhantomReference<Thing> thing = new PhantomReference<Thing>(new Thing(), queue);

Последним параметром конструктора является очередь queue. На самом деле вы можете использовать очередь не только с фантомными ссылками, но и со слабыми и мягкими ссылками.

Ссылка помещается в очередь перед тем, как объект на который она указывает будет финализирован и удален. Таким образом в методе finalize() объекта можно создать жесткую ссылку на удаляемый объект тем самым не дав ему умереть. В случае с фантомными ссылками это невозможно, т.к. она помещается в очередь уже по факту уничтожения объекта. Это и есть основное отличие фантомным ссылок от других.

На этом пожалуй и всё. Берегите память! ;)

4 комментария:

  1. А можно ли сделать гипер ссылку в ява книге например? т.е вставить ее в текст и чтоб она была кликабельна? или это фантастика))))))?

    ОтветитьУдалить
    Ответы
    1. Видимо слабо представляете. о чем говорите ))) Одно ясно точно, если "ява" - пишет пыхер....

      Удалить

Примечание. Отправлять комментарии могут только участники этого блога.