Использование Java ReferenceQueue

Действительно ли SoftReference и WeakReference помогают только при создании переменных экземпляра? Есть ли какая-либо польза от их использования в области метода?

Другая большая часть — ReferenceQueue. Помимо возможности отслеживать, какие ссылки определены как мусор, можно ли использовать Reference.enqueue() для принудительной регистрации объекта для сборки мусора?

Например, стоит ли создавать метод, который использует большие ресурсы памяти (удерживаемые сильными ссылками) в объекте и создает ссылки для их постановки в очередь?

Object bigObject;
public void dispose() {
    ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
    WeakReference<Object> ref = new WeakReference<Object>(bigObject, queue);
    bigObject = null;
    ref.enqueue();
}

(Представьте, что Object в этом случае представляет тип объекта, который использует много памяти... например, BufferedImage или что-то в этом роде)

Имеет ли это какой-то реальный эффект? Или это просто пустая трата кода?


person bgroenks    schedule 22.01.2013    source источник
comment
Слегка связанный: codereview.stackexchange.com/q/28534/4394   -  person Pacerier    schedule 19.09.2017


Ответы (4)


Одна общая идиома с эталонными очередями, например. подкласс WeakReference, чтобы прикрепить информацию, необходимую для очистки, а затем опросить ReferenceQueue, чтобы получить задачи очистки.

ReferenceQueue<Foo> fooQueue = new ReferenceQueue<Foo>();

class ReferenceWithCleanup extends WeakReference<Foo> {
  Bar bar;
  ReferenceWithCleanup(Foo foo, Bar bar) {
    super(foo, fooQueue);
    this.bar = bar;
  }
  public void cleanUp() {
    bar.cleanUp();
  }
}

public Thread cleanupThread = new Thread() {
  public void run() {
    while(true) {
      ReferenceWithCleanup ref = (ReferenceWithCleanup)fooQueue.remove();
      ref.cleanUp();
    }
  }
}

public void doStuff() {
  cleanupThread.start();
  Foo foo = new Foo();
  Bar bar = new Bar();
  ReferenceWithCleanup ref = new ReferenceWithCleanup(foo, bar);
  ... // From now on, once you release all non-weak references to foo,
      // then at some indeterminate point in the future, bar.cleanUp() will
      // be run. You can force it by calling ref.enqueue().
}

Например, внутреннее устройство реализации CacheBuilder в Guava при выборе weakKeys использует этот подход.

person Louis Wasserman    schedule 22.01.2013
comment
Значит, он ждет, пока ссылка не будет поставлена ​​в очередь, а затем вызывает cleanUp? Вызывают ли они когда-нибудь enqueue()? - person bgroenks; 22.01.2013
comment
Он также говорит, что GC не использует его. Так почему же он существует, интересно? - person bgroenks; 22.01.2013
comment
Если вы хотите вызвать его явно. Многие из этих вещей имеют чрезвычайно узкие варианты использования, но существуют потому, что их было бы невозможно получить, если они явно не предоставлены. - person Louis Wasserman; 22.01.2013
comment
Когда вы хотели бы явно вызвать его? - person bgroenks; 22.01.2013
comment
Я никогда не слышал об убедительном варианте использования, но, может быть, если вы хотите явно что-то очистить, независимо от того, действительно ли ссылка была GC'ed или нет. - person Louis Wasserman; 22.01.2013
comment
Хм. Думаю, я понимаю, почему это может кому-то понадобиться. По крайней мере, возможно, в контексте более крупной системы, которая зависела от результатов, возвращаемых ReferenceQueue. Спасибо за вашу помощь! - person bgroenks; 23.01.2013
comment
Вы бы явно поставили объект в очередь, если бы у вас был путь, включающий средство для вызова метода очистки, но вы использовали WeakReferences, а ReferenceQueue — это защита от исчерпания ресурсов из-за неаккуратного кода. Такие вещи, как сторонние классы пула соединений, сделают это. - person nsayer; 31.08.2015
comment
@LouisWasserman, это бесполезно. @nsayer, даже сторонним классам пула соединений это не нужно. enqueue — это неудачный API, используемый только кодом phail. - person Pacerier; 19.09.2017
comment
Есть ли причина, по которой в этом примере используется WeakReference, а не PhantonReference? Поскольку метод get ссылки никогда не вызывается, PhantonReference должен работать, и я предполагаю (хотя я не уверен в этом), что имеет смысл использовать самую слабую возможную ссылку, чтобы дать сборщику мусора максимальную гибкость. - person Dominique Unruh; 09.01.2018

Если у объекта есть только WeakReferences (или вообще нет ссылок!) на него, он может быть удален сборщиком мусора всякий раз, когда Java нужно освободить больше места в памяти. Таким образом, вы используете WeakReference всякий раз, когда хотите, чтобы объект оставался в памяти, но вам не нужно, чтобы он оставался ТАКИМ плохим (например, если Java нужно собрать его мусором, нет проблем, вы можете вернуть его как-нибудь и в то же время Java имеет лучшую производительность)

Постановка в очередь WeakReference позволяет выполнить итерацию ReferenceQueue и определить, какие ссылки были удалены сборщиком мусора, а какие нет. Вот и все — так что делайте это только в том случае, если вам нужно это знать.

Подробнее: http://weblogs.java.net/blog/2006/05/04/understanding-weak-references

person Patashu    schedule 22.01.2013
comment
Я знаю это. Но мой вопрос в том, заставляет ли метод enqueue() WeakReference собирать мусор или регистрироваться для сборки мусора? - person bgroenks; 22.01.2013
comment
Нет. Если вам нужна сборка мусора прямо сейчас, удалите все сильные ссылки и запустите сборщик мусора. - person Patashu; 22.01.2013
comment
Хотя я не думаю, что это гарантировано. Что на самом деле делает enqueue()? - person bgroenks; 22.01.2013
comment
@bgroenks Постановка в очередь относится к акту помещения элемента в хвост соответствующей очереди. Технически вы не можете заставить GC что-либо делать. Сборщик мусора никогда даже не вызывает метод enqueue(), он напрямую ставит в очередь ссылки. Единственный способ гарантировать, что объект подходит для сборки мусора, — это уничтожить все живые ссылки на этот объект. - person b1nary.atr0phy; 13.07.2013
comment
@Patashu, зачем мне нужна ReferenceQueue, чтобы определить, какие ссылки были GC-d, если я могу сделать if (weekReference.get() == null)? - person Gavriel; 21.02.2016
comment
@Gavriel Потому что цикл занятости, такой как while (weakReference.get() != null), сжигает слишком много ресурсов ЦП. ReferenceQueue позволяет вам спать, пока объект не будет доступен для завершения. - person Tavian Barnes; 13.09.2016
comment
@Patashu, поскольку WeakHashMap уже есть, правильно ли говорить, что ReferenceQueue бессмысленно? - person Pacerier; 18.09.2017

Одной из распространенных вещей является создание карт программных ссылок.

Map<String, SoftReference<BigThing>> cache = new HashMap<>();
Set<String> thingsIAmCurrentlyGetting = new HashSet<String>();
Object mutex = new Object();

BigThing getThing(String key) {
  synchronized(mutex) {
    while(thingsIAmCurrentlyGetting.contains(key)) {
      mutex.wait();
    }
    SoftReference<BigThing> ref = cache.get(key);
    BigThing bigThing = ref == null ? null : ref.get();
    if(bigThing != null) return bigThing;
    thingsIAmCurrentlyGetting.add(key);
  }

  BigThing bigThing = getBigThing(key); // this may take a while to run.

  synchronized(mutex) {
    cache.put(key, bigThing);
    thingsIAmCurrentlyGetting.remove(key);
    mutex.notifyAll();
  }

  return bigThing;
}

Я показываю здесь свою старую школу — в новых java-пакетах, вероятно, есть гораздо более изящные способы сделать это.

person PaulMurrayCbr    schedule 26.04.2014
comment
Ой, подождите, ваш вопрос был совсем не об этом! Это совершенно неважно! - person PaulMurrayCbr; 26.04.2014
comment
Честно говоря, это на самом деле очень хороший реальный пример того, как правильно использовать мягкую/слабую ссылку для правильной ленивой инициализации объектов (без условий гонки или блокировки), которая уведомляет другие ожидающие потоки... Он забыл сказать, что нет , создание и постановка ссылок в очередь совершенно не помогут и являются пустой тратой кода. Вам нужно, чтобы все ВАШИ ссылки без gc'd на большой объект были обнулены (чтобы они не мешали GC...) - person Ajax; 25.06.2016

Не уверен, в чем здесь вопрос, но:

1) soft ref пытается сохранить ссылку до тех пор, пока jvm действительно не понадобится память. Отлично подходит для кешей, особенно LRU. Посмотрите на множество примеров в Guava.

2) слабая ссылка вообще не пытается помешать gc освободить объект. Они используются, если вы хотите знать, используется ли этот объект где-либо еще. Например, они используются для хранения информации о потоках и классах, поэтому, когда поток или класс больше не используются, мы можем отбросить связанную с ними метаинформацию.

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

ReferenceQueue: туда ничего не помещается. gc сделает за вас. Они позволяют вам узнать, когда некоторые ссылки будут выпущены, без необходимости проверять их одну за другой.

person Uberto    schedule 25.08.2017
comment
Также фантомная ссылка блокирует собираемый объект, пока вы не очистите ссылку. Это не имеет смысла. - person Aleksandr Dubinsky; 18.10.2018