Подтвердить что ты не робот

Плюсы и минусы слушателей как WeakReferences

Каковы преимущества и недостатки слушателей как WeakReferences.

Большой "Pro", конечно, состоит в том, что:

Добавление слушателя как WeakReference означает, что слушателю не нужно беспокоить "удаление" себя.

Обновление

Для тех, кто беспокоится о слушателе, имеющем единственную ссылку на объект, почему не может быть 2 метода, addListener() и addWeakRefListener()?

те, кто не заботятся об удалении, могут использовать последние.

4b9b3361

Ответ 1

Прежде всего, использование WeakReference в списках слушателей даст вашему объекту различный семантический, а затем использование жестких ссылок. В случае с жесткой ссылкой addListener (...) означает "уведомлять предоставленный объект о конкретных событиях , пока я не остановлю его явно с помощью removeListener (..)", в случае с слабыми ссылками это означает, уведомлять предоставленный объект о конкретных событиях , пока этот объект не будет использоваться кем-либо другим (или явно остановится с помощью removeListener) ". Обратите внимание: во многих ситуациях совершенно законно иметь объект, прислушиваться к некоторым событиям и не иметь других ссылок, удерживающих его от GC. Примером может служить Logger.

Как вы можете видеть, использование WeakReference не просто решает одну проблему ( "я должен помнить, чтобы не забывать удалять добавленный прослушиватель где-то" ), но и поднимать другую - "Я должен помнить, что мой слушатель может остановиться слушайте в любой момент, когда больше нет ссылки на него". Вы не решаете проблему, вы просто торгуете одной проблемой для другой. Посмотрите, каким-либо образом, вы вынуждены четко определять, разрабатывать и отслеживать прожиточный период слушателя - так или иначе.

Итак, я согласен с тем, что использование WeakReference в списках слушателей скорее похоже на взломать, чем решение. Это шаблон, о котором стоит знать, иногда он может помочь вам, например, сделать код устаревшего кода. Но это не шаблон выбора:)

P.S. Также следует отметить, что WeakReference вводит дополнительный уровень косвенности, который в некоторых случаях с чрезвычайно высокой частотой событий может снизить производительность.

Ответ 2

Это не полный ответ, но сама сила, которую вы цитируете, также может быть ее основной слабостью. Подумайте, что произойдет, если прослушиватели действий будут реализованы слабо:

button.addActionListener(new ActionListener() {
    // blah
});

Этот слушатель будет собирать мусор в любой момент! Не редкость, что единственная ссылка на анонимный класс - это событие, к которому вы добавляете его.

Ответ 3

Я видел тонны кода, где слушатели не были незарегистрированы должным образом. Это означает, что они по-прежнему называются без необходимости выполнять ненужные задачи.

Если только один класс полагается на слушателя, тогда его легко очистить, но что происходит, когда 25 классов полагаются на него? Становится намного сложнее отменить регистрацию их должным образом. Дело в том, что ваш код может начинаться с одного объекта, ссылающегося на слушателя, и заканчиваться в будущей версии с 25 объектами, ссылающимися на тот же самый слушатель.

Не использовать WeakReference эквивалентно риску потребления ненужной памяти и процессора. Это сложнее, сложнее и требует больше работы с жесткими ссылками в сложном коде.

WeakReferences полны профи, потому что они очищаются автоматически. Единственный аргумент в том, что вы не должны забывать держать твердую ссылку в другом месте вашего кода. Как правило, это было бы в объектах, полагающихся на этого слушателя.

Я ненавижу код, создающий анонимные экземпляры экземпляров слушателей (как упоминал Кирк Волл), потому что после регистрации вы больше не можете отменить регистрацию этих слушателей. У вас нет ссылки на них. Это действительно плохое кодирование IMHO.

Вы также можете null ссылаться на слушателя, когда он вам больше не нужен. Вам больше не нужно об этом беспокоиться.

Ответ 4

На самом деле нет профи. Слабая защита обычно используется для "необязательных" данных, таких как кеш, где вы не хотите предотвращать сбор мусора. Вы не хотите, чтобы ваш мусорщик был собран, вы хотите, чтобы он продолжал слушать.

Обновление:

Хорошо, думаю, я мог бы понять, к чему вы клоните. Если вы добавляете недолговечных слушателей к долгоживущим объектам, может быть полезно использовать weakReference. Например, если вы добавляли PropertyChangeListeners в свои объекты домена, чтобы обновить состояние графического интерфейса, который постоянно воссоздается, объекты домена будут привязаны к графическим интерфейсам, которые могут быть созданы. Подумайте о большом всплывающем диалоговом окне, которое постоянно воссоздается, а ссылка на слушателя возвращается к объекту Employee через PropertyChangeListener. Исправьте меня, если я ошибаюсь, но я не думаю, что весь шаблон PropertyChangeListener очень популярен.

С другой стороны, если вы говорите о прослушивателях между элементами GUI или объектах домена, которые слушают элементы GUI, вы ничего не будете покупать, так как когда GUI уйдет, так же будут и слушатели.

Вот пара интересных замечаний:

http://www.javalobby.org/java/forums/t19468.html

Как разрешить утечку памяти получателя swing?

Ответ 5

Честно говоря, я действительно не покупаю эту идею и точно, что вы ожидаете от addWeakListener. Может быть, это только я, но это кажется неправильной хорошей идеей. Сначала это соблазняет, но проблемы, которые он может подразумевать, не являются незначительными.

С weakReference вы не уверены, что слушатель больше не будет вызываться, когда сам слушатель больше не ссылается. Сборщик мусора может освободить людей через несколько минут позже или никогда. Это означает, что он может продолжать потреблять процессор и делать странным это как исключение бросания, потому что слушатель не должен вызываться.

Примером с swing было бы попытаться сделать то, что вы можете сделать, только если ваш компонент пользовательского интерфейса фактически привязан к активному окну. Это может вызвать исключение и повлиять на уведомитель, что приведет к сбою и предотвращению недопущения действительных слушателей.

Вторая проблема, как уже было сказано, является анонимным слушателем, их можно было высвободить слишком рано, никогда не уведомляться вообще или только несколько раз.

То, что вы пытаетесь достичь, опасно, поскольку вы больше не можете контролировать, когда перестаете получать уведомления. Они могут длиться вечно или слишком скоро.

Ответ 6

Поскольку вы добавляете слушателя WeakReference, я предполагаю, что вы используете собственный объект Observable.

Имеет смысл использовать WeakReference для объекта в следующей ситуации. - В объекте Observable есть список слушателей. - У вас уже есть твердая ссылка на слушателей в другом месте. (вы должны быть уверены в этом) - Вы не хотите, чтобы сборщик мусора прекратил очищать слушателей только потому, что в Наблюдателе есть ссылка на него. - Во время сбора мусора слушатели будут очищены. В методе, в котором вы уведомляете слушателей, вы очищаете объекты WeakReference из списка уведомлений.

Ответ 7

Я не могу придумать какой-либо законный прецедент для использования WeakReferences для слушателей, если каким-то образом ваш случай использования не включает слушателей, которые явно не должны существовать после следующего цикла GC (в этом случае использование, конечно, будет VM/platform специфические).

Можно представить несколько более законный вариант использования для SoftReferences, где слушатели являются необязательными, но занимают много кучи и должны быть первыми, когда размер свободной кучи начинает становиться рыхлым. Возможно, какой-то дополнительный кеширование или другой тип вспомогательного слушателя может быть кандидатом. Даже тогда кажется, что вы хотите, чтобы внутренности слушателей использовали SoftReferences, а не связь между слушателем и слушателем.

В общем случае, если вы используете постоянный шаблон прослушивателя, слушатели не являются необязательными, поэтому задавать этот вопрос может быть признаком того, что вам нужно пересмотреть свою архитектуру.

Это академический вопрос, или у вас есть практическая ситуация, которую вы пытаетесь решить? Если это практическая ситуация, мне бы хотелось услышать, что это такое - и вы могли бы получить больше, менее абстрактные советы о том, как ее решить.

Ответ 8

По-моему, это хорошая идея в большинстве случаев. Код, который отвечает за освобождение слушателя, находится там же, где он регистрируется.

На практике я вижу много программного обеспечения, которое постоянно удерживает слушателей. Часто программисты даже не знают, что они должны отменить регистрацию.

Обычно можно вернуть пользовательский объект со ссылкой на слушателя, который позволяет манипулировать тем, когда нужно отменить регистрацию. Например:

listeners.on("change", new Runnable() {
  public void run() {
    System.out.println("hello!");
  }
}).keepFor(someInstance).keepFor(otherInstance);

этот код регистрирует слушателя, возвращает объект, который инкапсулирует слушателя и имеет метод keep keepFor, который добавляет слушателя к статическому weakHashMap с параметром экземпляра в качестве ключа. Это гарантирует, что слушатель зарегистрирован, по крайней мере, до тех пор, пока someInstance и otherInstance не собираются с мусором.

Могут быть другие методы, такие как keepForever() или keepUntilCalled (5) или keepUntil (DateTime.now(). plusSeconds (5)) или unregisterNow().

Значение по умолчанию может сохраняться навсегда (до незарегистрированного).

Это также может быть реализовано без слабых ссылок, но phantom ссылки, которые вызывают удаление слушателя.

edit: создал небольшую библиотеку, которая реализует базовую версию этого aproach https://github.com/creichlin/struwwel

Ответ 9

WeakListeners полезны в ситуациях, когда вы специально хотите, чтобы GC контролировал время жизни слушателя.

Как указано выше, это действительно другая семантика по сравнению с обычным случаем addListener/removeListener, но она действительна в некоторых сценариях.

Например, рассмотрим очень большое дерево, которое является разреженным - некоторые уровни узлов явно не определены, но могут быть выведены из родительских узлов, расположенных дальше по иерархии. Неявно определенные узлы прослушивают те родительские узлы, которые определены так, что сохраняют актуальную имплицированную/унаследованную ценность. Но дерево огромно - мы не хотим, чтобы подразумеваемые узлы были навсегда - до тех пор, пока они используются вызывающим кодом, плюс, возможно, кеш LRU за несколько секунд, чтобы не переваривать одни и те же значения снова и снова.

Здесь слабый прослушиватель позволяет дочерним узлам прослушивать родителей, а также определяет их жизненное решение по достижимости/кешированию, поэтому структура не поддерживает все подразумеваемые узлы в памяти.

Ответ 10

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

Кажется, мы вспомнили, что у нас были проблемы с одним из наших пользовательских прослушивателей PropertyChangeSupport, которые использовались внутри строк Views в нашем ListView. Мы не смогли найти хороший и надежный способ отменить регистрацию этих слушателей, поэтому использование слушателя WeakReference казалось самым чистым решением.

Ответ 11

Из тестовой программы видно, что анонимные ActionListeners не будут препятствовать сбору мусора:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;

public class ListenerGC {

private static ActionListener al = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.err.println("blah blah");
        }
    };
public static void main(String[] args) throws InterruptedException {

    {
        NoisyButton sec = new NoisyButton("second");
        sec.addActionListener(al);
        new NoisyButton("first");
        //sec.removeActionListener(al);
        sec = null;
    }
    System.out.println("start collect");
    System.gc( );
    System.out.println("end collect");
    Thread.sleep(1000);
    System.out.println("end program");
}

private static class NoisyButton extends JButton {
    private static final long serialVersionUID = 1L;
    private final String name;

    public NoisyButton(String name) {
        super();
        this.name = name;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println(name + " finalized");
        super.finalize();
    }
}
}

дает:

start collect
end collect
first finalized
second finalized
end program

Ответ 12

У меня есть 3 предложения для оригинального плаката. Извините за воскрешение старой темы, но я думаю, что мои решения ранее не обсуждались в этом потоке.

Во-первых, Рассмотрим следующий пример javafx.beans.values.WeakChangeListener в библиотеках JavaFX.

Во-вторых, Я один повысил шаблон JavaFX, изменив методы addListener моего Observable. Новый метод addListener() теперь создает экземпляры соответствующих классов WeakXxxListener для меня.

Метод "событие пожара" был легко изменен, чтобы разыменовать XxxWeakListeners и удалить их, когда WeakReference.get() вернул null.

Теперь метод удаления был немного более неприятным, так как мне нужно итерировать весь список, а это значит, что мне нужно выполнить синхронизацию.

В-третьих, До внедрения этой стратегии я использовал другой метод, который может оказаться полезным. (Слушатели с твердой ссылкой) получили новое событие, которое они проверили на предмет того, используются ли они еще или нет. Если нет, то они не подписались от наблюдателя, который позволил им быть GCed. Для недолговечных слушателей, подписавшихся на долгоживущие наблюдатели, обнаружение устаревания было довольно легко.

В знак уважения к людям, которые утверждали, что "хорошая практика программирования всегда отменяет подписку на ваших слушателей, всякий раз, когда слушатель прибегает к отписанию себя, я обязательно создал запись в журнале и позже исправил проблему в своем коде.