-2

Может ли слушатель поддерживать слабые ссылки слушателей решить мои проблемы с памятью?Поддержка Java-слушателей со слабыми реферированными ресиверами решает проблемы с памятью?

+2

Вам нужно сформулировать это как вопрос/ответ. – aioobe

+3

StackOverflow на самом деле не место для написания блога. – ceejayoz

+0

http: //blog.stackoverflow.com/2011/07/the-ok-to-ask-and-answer-your-own-questions/ – sp00m

ответ

1

Я сделал немного, чтобы проверить это сам (см. Ниже). Согласны ли вы с моими выводами?

Введение:

Вот 7 различных испытаний, демонстрирующие использование и промаха-использование сильных и слабых ссылок в контексте модели наблюдателя (модель слушателя). Я знаю, что уже есть много сообщений о профи & минусов из слабых ссылок, но даже после измельчения моего пути через тонны этих я все еще чувствовал, что мне нужно чувствовать себя с кодом. Моя потребность в знаниях была вызвана встречей утечек кучи при масштабировании 10-15-летнего кодового пула с потерей слушателей (не-gui-слушателей). Мысль о том, что результаты и код могут быть полезны для других, желающих получить четкое представление по этой теме.

Комментарии/исправления/альтернативы приветствуются!


Вывод:

Всегда использовать один из двух стратегий:

  • сильные ссылки на слушателей, а затем убедитесь, что каждый из них удаляется перед слушателями «владельца» (или владельцы владельцев ...) отбрасывается и становится недоступным (пример C ниже). Это означает строгое использование (и использование!) Методов «free()» или «destroy()» практически для всех ваших классов.
  • Слабые ссылки на слушатели, которые являются полями на их классе владельца (то есть слушатели не должны быть локальными переменными метода) (пример E ниже).

Если вы создаете библиотеку для использования другими, слабая ссылка на слушателей, вероятно, не вариант, потому что ни во время компиляции, ни среда выполнения Java имеет какой-либо способ проверки того, что слушатель регистрации на субъект имеет прочную связь в другом конце. Обратите внимание, что вопреки многому обсуждению проблема не имеет ничего общего с тем, является ли слушатель анонимным классом. Речь идет исключительно о том, как слушатель подключается к «другому концу» (конец, который не находится на стороне, а на стороне наблюдателя).


Вот моя консоль выход:

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

A. WITHOUT connecting listeners 
     At start, memory used = 281 KB 
     After instantiation 100 observers with a double[1000000] each, memory used = 800288 KB 
     After setting list of observes to null, memory used = 282 KB 

    B. STRONG references to FIELD listeners 
     At start, memory used = 286 KB 
     After instantiation 100 observers with a double[1000000] each, memory used = 800291 KB 
     After making change on subject (notifiedCount=100), memory used = 800292 KB 
     After setting list of observes to null, memory used = 800292 KB 

    C. STRONG references to FIELD listeners with REMOVE 
     At start, memory used = 286 KB 
     After instantiation 100 observers with a double[1000000] each, memory used = 800292 KB 
     After making change on subject (notifiedCount=100), memory used = 800293 KB 
     After removing listeners, memory used = 800292 KB 
     After setting list of observes to null, memory used = 286 KB 

    D. STRONG references to LOCAL listeners 
     At start, memory used = 286 KB 
     After instantiation 100 observers with a double[1000000] each, memory used = 800295 KB 
     After making change on subject (notifiedCount=100), memory used = 800295 KB 
     After setting list of observes to null, memory used = 800294 KB 

    E. WEAKLY references to FIELD listeners 
     At start, memory used = 287 KB 
     After instantiation 100 observers with a double[1000000] each, memory used = 800297 KB 
     After making change on subject (notifiedCount=100), memory used = 800297 KB 
     After setting list of observes to null, memory used = 291 KB 

    F. WEAKLY references to FIELD listeners with REMOVE 
     At start, memory used = 287 KB 
     After instantiation 100 observers with a double[1000000] each, memory used = 800297 KB 
     After making change on subject (notifiedCount=100), memory used = 800297 KB 
     After removing listeners, memory used = 800294 KB 
     After setting list of observes to null, memory used = 288 KB 

    G. WEAKLY references to LOCAL listeners 
     At start, memory used = 287 KB 
     After instantiation 100 observers with a double[1000000] each, memory used = 800297 KB 
     After making change on subject (notifiedCount=0), memory used = 800297 KB 
     After setting list of observes to null, memory used = 291 KB 

Что я сделал:

ШАГ 1: я создал два класса поддержки слушателя:

  • один с классическими сильными ссылками на своих слушателей:
public class ListenerSupportWithStrongReferences implements ListenerSupport { 
    List&ltPropertyChangeListener> strongListeners = new ArrayList&ltPropertyChangeListener> 

    public void addPropertyChangeListener(
      PropertyChangeListener propertyChangeListener) { 
     strongListeners.add(propertyChangeListener); 
    } 

    public void removePropertyChangeListener(
      PropertyChangeListener propertyChangeListener) { 
     strongListeners.remove(propertyChangeListener); 
    } 

    public void firePropertyChangeEvent(PropertyChangeEvent propertyChangeEvent) { 
     for(PropertyChangeListener strongListener : strongListeners){ 
      strongListener.propertyChange(propertyChangeEvent); 
     } 
    } 
} 
  • , а другой со слабыми ссылками на своих слушателей:
public class ListenerSupportWithWeakReferences implements ListenerSupport { 

    List&ltWeakReference&ltPropertyChangeListener>> weakListeners = 
     new ArrayList&ltWeakReference&ltPropertyChangeListener>>(); 

    public void addPropertyChangeListener(
      PropertyChangeListener propertyChangeListener) { 
     weakListeners.add(new WeakReference&ltPropertyChangeListener>(propertyChangeListener)); 
    } 

    public void removePropertyChangeListener(
      PropertyChangeListener propertyChangeListener) { 
     for(WeakReference&ltPropertyChangeListener> weakReference : weakListeners){ 
      if(weakReference.get()==propertyChangeListener){ 
       weakListeners.remove(weakReference); 
       break; 
      } 
     } 

    } 

    public void firePropertyChangeEvent(PropertyChangeEvent propertyChangeEvent) { 
     for(WeakReference&ltPropertyChangeListener> weakReference : weakListeners){ 
      PropertyChangeListener propertyChangeListener = weakReference.get(); 
      if(propertyChangeListener!=null) 
       propertyChangeListener.propertyChange(propertyChangeEvent); 
     } 
    } 
} 

оба класса поддержки реализуют интерфейс:

public interface ListenerSupport extends SubjectInterface { 

    public void firePropertyChangeEvent(PropertyChangeEvent propertyChangeEvent); 
} 

, который в свою очередь, расширяет интерфейс:

public interface SubjectInterface { 

    public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener); 
    public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener); 
} 

ШАГ 2: Я создал конкретный предметный класс, позволяющий мне контролировать тип поддержки Listerner через конструктор и на котором я мог бы изменить значение, которое уведомляло бы слушателей, используя любую поддержку слушателя.

public class ConcreteSubject implements SubjectInterface { 

    public static final String STRONG_LISTENERS = "STRONG_LISTENERS"; 
    public static final String WEAK_LISTENERS = "WEAK_LISTENERS"; 

    private final ListenerSupport listenerSupport; 
    private int myValue; 

    public ConcreteSubject(String typeOfListeners) { 
     if(typeOfListeners.equals(STRONG_LISTENERS)){ 
      listenerSupport = new ListenerSupportWithStrongReferences(); 
     } else if(typeOfListeners.equals(WEAK_LISTENERS)){ 
      listenerSupport = new ListenerSupportWithWeakReferences(); 
     } else { 
      throw new RuntimeException("Unknown type of listeners:"+typeOfListeners); 
     } 
    } 

    public void addPropertyChangeListener(
      PropertyChangeListener propertyChangeListener) { 
     listenerSupport.addPropertyChangeListener(propertyChangeListener); 
    } 

    public void removePropertyChangeListener(
      PropertyChangeListener propertyChangeListener) { 
     listenerSupport.removePropertyChangeListener(propertyChangeListener); 
    } 

    private void fireMyValueHasChange(int oldValue, int newValue){ 
     PropertyChangeEvent propertyChangeEvent = 
      new PropertyChangeEvent(this, "SomeEvent", new Integer(oldValue), new Integer(newValue)); 
     listenerSupport.firePropertyChangeEvent(propertyChangeEvent); 
    } 

    public int getMyValue() { 
     return myValue; 
    } 

    public void setMyValue(int myValue) { 
     int oldValue = this.myValue; 
     this.myValue = myValue; 
     fireMyValueHasChange(oldValue, this.myValue); 
    } 
} 

ШАГ 3: Я создал класс наблюдений с большим двойным массивом так, чтобы занять заметное количество памяти. Я также дал классу наблюдения возможность обслуживать двух разных слушателей: - слушатель поля, созданный при создании самого наблюдателя (поэтому наблюдатель имел сильную ссылку на его слушателя) и - слушатель, созданный в методе, возвращающем его (поэтому наблюдатель не имел ссылки на своего слушателя). Я также создал счетчик для отслеживания уведомлений.

public class ObservingObject { 

    public final static int DOUBLE_ARRAY_SIZE = 1000000; 
    private double[] myDoubles = new double[DOUBLE_ARRAY_SIZE]; 
    private int notifiedCount = 0; 

    private PropertyChangeListener fieldListener = new PropertyChangeListener() { 
     public void propertyChange(PropertyChangeEvent arg0) { 
      notifiedCount++; 
     } 
    }; 

    public PropertyChangeListener getFieldPropertyChangeListener() { 
     return fieldListener; 
    } 

    public PropertyChangeListener getLocalMethodPropertyChangeListener() { 
     PropertyChangeListener localListenerInstance = new PropertyChangeListener() { 
      public void propertyChange(PropertyChangeEvent arg0) { 
       notifiedCount++; 
      } 
     }; 
     return localListenerInstance; 
    } 

    public int getNotifiedCount() { 
     return notifiedCount; 
    } 
} 

ШАГ 4: Наконец, я создал свой главный класс, чтобы опробовать 7 различных "конфигураций" из:

  • сильных/слабых ссылок на слушателей,
  • поле/метод локального слушатели,
  • с/без rempoving слушателей.

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

public class WeakReferenceListenerTrials { 

    private static final String LOCAL_LISTENER = "local method listener"; 
    private static final String FIELD_LISTENER = "field listener"; 
    List&ltObservingObject> observers; 

    private void runTials() { 
     runTrial("A. WITHOUT connecting listeners", null, null, false); 
     runTrial("B. STRONG references to FIELD listeners", 
       new ConcreteSubject(ConcreteSubject.STRONG_LISTENERS), FIELD_LISTENER, false); 
     runTrial("C. STRONG references to FIELD listeners with REMOVE", 
       new ConcreteSubject(ConcreteSubject.STRONG_LISTENERS), FIELD_LISTENER, true); 
     runTrial("D. STRONG references to LOCAL listeners", 
       new ConcreteSubject(ConcreteSubject.STRONG_LISTENERS), LOCAL_LISTENER, false); 
     runTrial("E. WEAKLY references to FIELD listeners", 
       new ConcreteSubject(ConcreteSubject.WEAK_LISTENERS), FIELD_LISTENER, false); 
     runTrial("F. WEAKLY references to FIELD listeners with REMOVE", 
       new ConcreteSubject(ConcreteSubject.WEAK_LISTENERS), FIELD_LISTENER, true); 
     runTrial("G. WEAKLY references to LOCAL listeners", 
       new ConcreteSubject(ConcreteSubject.WEAK_LISTENERS), LOCAL_LISTENER, false); 
    } 

    private void runTrial(String titleText, 
      ConcreteSubject subject, String localOrFieldListeners, boolean removeListeners) { 
     System.out.println("\n"+titleText); 
     printMem(" At start"); 

     instantiateObserversAndConnectListenersToSubject(subject, localOrFieldListeners); 
     printMem(" After instantiation 100 observers with a double["+ObservingObject.DOUBLE_ARRAY_SIZE+"] each"); 

     if(subject!=null){ 
      subject.setMyValue(1); 
      int notifiedCount = 0; 
      for(ObservingObject observingObject : observers){ 
       notifiedCount = notifiedCount + observingObject.getNotifiedCount(); 
      } 
      printMem(" After making change on subject (notifiedCount="+notifiedCount+")"); 
     } 

     if(removeListeners){ 
      removeListeners(subject, localOrFieldListeners); 
      printMem(" After removing listeners"); 
     } 

     observers = null; 
     printMem(" After setting list of observes to null"); 
    } 

    private void removeListeners(ConcreteSubject subject, 
      String localOrFieldListeners) { 
     if(localOrFieldListeners.equals(FIELD_LISTENER) && subject!=null){ 
      for(ObservingObject observingObject :observers){ 
       subject.removePropertyChangeListener(observingObject.getFieldPropertyChangeListener()); 
      } 
     } 
    } 

    private void instantiateObserversAndConnectListenersToSubject(
      ConcreteSubject subject, String localOrFieldListeners) { 
     observers = new ArrayList&ltObservingObject>(); 
     int observerCount = 100; 
     for(int i = 0 ; i lt observerCount ; i++){ 
      ObservingObject observingObject = new ObservingObject(); 
      observers.add(observingObject); 
      if(subject!=null){ 
       if(localOrFieldListeners.equals(FIELD_LISTENER)){ 
        subject.addPropertyChangeListener(observingObject.getFieldPropertyChangeListener()); 
       } else if(localOrFieldListeners.equals(LOCAL_LISTENER)){ 
        subject.addPropertyChangeListener(observingObject.getLocalMethodPropertyChangeListener()); 
       } else { 
        throw new RuntimeException("Unknow listener type"); 
       } 
      } 
     } 
    } 

    private void printMem(String string) { 
     System.out.println(string+", memory used = "+getUsedMemoryInKB()+" KB"); 
    } 

    private static int getUsedMemoryInKB() { 
     // Start by garbage collect 
     Runtime runtime = Runtime.getRuntime(); 
     runtime.gc(); 

     // Calculate 'used memory' as difference between total and free 
     long totalMemory = runtime.totalMemory(); 
     long freeMemory = runtime.freeMemory(); 
     long usedMemory = totalMemory - freeMemory; 
     int usedMemoryInKB = (int)(usedMemory/1000); 
     return usedMemoryInKB; 
    } 

    public static void main(String[] args) { 
     new WeakReferenceListenerTrials().runTials(); 
    } 
} 

Вот так. Надеюсь, что это полезно для других. Это было для меня.