Статья для понимания Reference и ссылочных типов в java
Введение
В Java есть как типы значений, так и ссылочные типы. Ссылочные типы обычно предназначены для объектов в Java. Сегодня я расскажу о ссылочных типах в Java. Java определяет класс Reference для ссылочных типов. Reference — это класс, тесно связанный с механизмом сборки мусора Java.Обсуждая реализацию Reference, вы можете глубже понять, как работает сборка мусора Java.
Эта статья начинается с четырех типов ссылок в java и шаг за шагом раскрывает их.
Четыре типа ссылок в Java: сильная ссылка, мягкая ссылка, слабая ссылка и виртуальная ссылка.
Сильная ссылка
Ссылка в java является строгой ссылкой по умолчанию, и операция присваивания любого объекта создает сильную ссылку на объект.
Давайте посмотрим на пример:
public class StrongReferenceUsage {
@Test
public void stringReference(){
Object obj = new Object();
}
}
Выше мы создали объект Object и присвоили его obj, что является строгой ссылкой на new Object().
Особенность сильных ссылок заключается в том, что пока есть сильные ссылки, объект, на который ссылаются, не будет удален сборщиком мусора.
Мягкая ссылка
Мягкая ссылка имеет специальный тип SoftReference в java.Мягкая ссылка означает, что объект, на который ссылаются, будет переработан только при нехватке памяти.
Сначала взгляните на определение SoftReference:
public class SoftReference<T> extends Reference<T>
SoftReference наследуется от Reference. Он имеет два конструктора:
public SoftReference(T referent)
а также:
public SoftReference(T referent, ReferenceQueue<? super T> q)
Первый параметр хорошо известен, он является объектом мягкой ссылки. Второй параметр называется ReferenceQueue, который используется для хранения инкапсулированных объектов Reference, подлежащих повторному использованию. Объекты в ReferenceQueue обрабатываются внутренним классом ReferenceHandler в Справочный класс.
Возьмем пример SoftReference:
@Test
public void softReference(){
Object obj = new Object();
SoftReference<Object> soft = new SoftReference<>(obj);
obj = null;
log.info("{}",soft.get());
System.gc();
log.info("{}",soft.get());
}
Выходной результат:
22:50:43.733 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4
22:50:43.749 [main] INFO com.flydean.SoftReferenceUsage - java.lang.Object@71bc1ae4
Видно, что в случае достаточного объема памяти объект, на который ссылается SoftReference, не будет переработан.
слабая ссылка
weakReference очень похож на softReference, разница в том, что объект, на который ссылается weekReference, будет перерабатываться до тех пор, пока выполняется сборка мусора, независимо от того, недостаточно ли памяти.
Тот же WeakReference также имеет два конструктора:
public WeakReference(T referent);
public WeakReference(T referent, ReferenceQueue<? super T> q);
Смысл тот же, что и в SoftReference, поэтому повторяться здесь не будем.
Давайте рассмотрим пример слабой ссылки:
@Test
public void weakReference() throws InterruptedException {
Object obj = new Object();
WeakReference<Object> weak = new WeakReference<>(obj);
obj = null;
log.info("{}",weak.get());
System.gc();
log.info("{}",weak.get());
}
Выходной результат:
22:58:02.019 [main] INFO com.flydean.WeakReferenceUsage - java.lang.Object@71bc1ae4
22:58:02.047 [main] INFO com.flydean.WeakReferenceUsage - null
Мы видим, что после сборки мусора слабоссылочные объекты перерабатываются.
PhantomReference
Роль PhantomReference заключается в отслеживании активности сборщика мусора по сбору объектов.Во время процесса GC, если PhantomReference найден, GC помещает ссылку в ReferenceQueue, которая будет обрабатываться программистом.Когда программист вызывает метод ReferenceQueue.pull(), после удаления ссылки из ReferenceQueue объект Reference станет неактивным, что означает, что объект, на который ссылаются, может быть переработан.
В отличие от SoftReference и WeakReference, у PhantomReference всего один конструктор, который нужно передать в ReferenceQueue:
public PhantomReference(T referent, ReferenceQueue<? super T> q)
См. пример PhantomReference:
@Slf4j
public class PhantomReferenceUsage {
@Test
public void usePhantomReference(){
ReferenceQueue<Object> rq = new ReferenceQueue<>();
Object obj = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<>(obj,rq);
obj = null;
log.info("{}",phantomReference.get());
System.gc();
Reference<Object> r = (Reference<Object>)rq.poll();
log.info("{}",r);
}
}
результат операции:
07:06:46.336 [main] INFO com.flydean.PhantomReferenceUsage - null
07:06:46.353 [main] INFO com.flydean.PhantomReferenceUsage - java.lang.ref.PhantomReference@136432db
Мы видим, что значение get равно null, а после GC у poll есть значение.
Поскольку PhantomReference ссылается на объекты, которые необходимо удалить сборщиком мусора, в определении класса get всегда возвращает значение null:
public T get() {
return null;
}
Ссылка и ReferenceQueue
Поговорив о вышеупомянутых четырех видах ссылок, давайте поговорим о роли их родительских классов Reference и ReferenceQueue.
Ссылка — это абстрактный класс, у каждой Ссылки есть указанный объект, в Ссылке есть 5 очень важных свойств: референт, следующий, обнаруженный, ожидающий, очередь.
private T referent; /* Treated specially by GC */
volatile ReferenceQueue<? super T> queue;
Reference next;
transient private Reference<T> discovered; /* used by VM */
private static Reference<Object> pending = null;
Каждая ссылка может рассматриваться как узел, а несколько ссылок связаны через три атрибута: следующая, обнаруженная и ожидающая.
Сначала используйте изображение, чтобы иметь общую концепцию ссылки:
referent — это объект, на который ссылается Reference.
С помощью следующего свойства можно создать ReferenceQueue.
С помощью обнаруженного свойства вы можете создать список обнаруженных объектов.
Через свойство pending вы можете создать список ожидания.
четыре штата
Прежде чем говорить об этих трех Очереди/Списке, давайте поговорим о четырех состояниях ссылки:
Из приведенной выше диаграммы видно, что ссылка может иметь четыре состояния.
Поскольку у Reference есть два конструктора, один с ReferenceQueue и один без него.
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
Для ссылки с ReferenceQueue сборщик мусора поместит ссылку объекта, подлежащего повторному использованию, в ReferenceQueue, а последующая ссылка должна быть обработана программистом (вызовом метода опроса).
Ссылка без ReferenceQueue обрабатывается самим GC, и статус ссылки объекта, подлежащего повторному использованию, станет неактивным.
После создания ссылки войдите в активное состояние.
В активном состоянии, если доступное состояние объекта, на который делается ссылка, изменится, он перейдет в состояние «Неактивно» или «Ожидание».
Неактивное состояние хорошо понятно.Эталонное состояние, достигшее неактивного состояния, не может быть изменено и будет ожидать перезапуска GC.
Состояние Pending означает ожидание входа в очередь.В ссылке есть ReferenceHandler, который вызовет метод постановки в очередь, чтобы поместить объект Pending в очередь.
Состояние объекта, поступающего в очередь, становится поставленным в очередь.
Если объект в состоянии Enqueued удаляется из ReferenceQueue путем вызова метода опроса, состояние ссылки становится неактивным, ожидая перезапуска GC.
Это полный жизненный цикл ссылки.
Три очереди/списка
С учетом концепций вышеупомянутых четырех состояний давайте поговорим о трех очередях/списках: ReferenceQueue, обнаруженном списке и ожидающем списке.
ReferenceQueue уже упоминался, когда речь шла о состоянии, по сути, оно связано с next в Reference. Используется для хранения объектов, которые должны быть собраны сборщиком мусора.
Ожидающий список — это список, который нужно ввести в ReferenceQueue.
Обнаруженный список немного отличается: в состоянии Pending обнаруженный список равен ожидающему списку.
В активном состоянии обнаруженный список фактически поддерживает цепочку ссылок. Через эту цепочку ссылок мы можем получить структуру цепочки ссылок.Когда состояние ссылки больше не является активным, ссылку необходимо удалить из обнаруженного списка.
WeakHashMap
Наконец, давайте поговорим о WeakHashMap.WeakHashMap аналогичен WeakReference.Если ключ в WeakHashMap больше не используется и ему присвоено значение null, запись, соответствующая ключу, будет автоматически удалена из WeakHashMap.
Возьмем пример:
@Test
public void useWeakHashMap(){
WeakHashMap<Object, Object> map = new WeakHashMap<>();
Object key1= new Object();
Object value1= new Object();
Object key2= new Object();
Object value2= new Object();
map.put(key1, value1);
map.put(key2, value2);
log.info("{}",map);
key1 = null;
System.gc();
log.info("{}",map);
}
Выходной результат:
[main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc, java.lang.Object@11028347=java.lang.Object@1f89ab83}
[main] INFO com.flydean.WeakHashMapUsage - {java.lang.Object@14899482=java.lang.Object@2437c6dc}
Видно, что после gc WeakHashMap имеет только одну Entry.
Суммировать
В этой статье объясняются 4 типа ссылок в Java и подробно обсуждается внутренний механизм ссылок. Заинтересованные друзья могут оставить сообщение для совместного обсуждения.
Примеры этой статьиGitHub.com/Dadean2009/приходите…
Добро пожаловать, обратите внимание на мой публичный номер: вас ждут самые интересные вещи о программе! Для получения дополнительной информации, пожалуйста, посетитеwww.flydean.com