답변

1

나는 그것을 스스로 체크 아웃하기 위해 약간을했다. (아래 참조). 내 결론에 동의합니까?

소개 :

여기 관찰자 모델 (수신기 모델)의 맥락에서 강하고 약한 참조의 사용 용도 및 양을 보여주는 7 개 가지 실험이다. 나는 이미 거기에 약함에 대한 많은 게시물이 있다는 것을 알고있다. 그러나 심지어 이것들의 톤을 통해 자신의 길을 갈고 닦은 후에도 나는 여전히 내가 느끼는 느낌이 필요하다고 느꼈다. 10-15 년의 코드 풀을 청취자 (비 GUI 사용자)가 잃어 버렸을 때 힙 누수가 발생하여 필자에게 알릴 필요가있었습니다. 결과와 코드가이 주제에 대해 명확 해지기를 원하는 사람들에게 유용 할 것이라고 생각했습니다.

의견/수정/다른 대안을 환영합니다!


결론 :

항상 사용하는 두 가지 전략 중 하나를 다음

  • 강한 청취자에 대한 참조와 그들의 모든 하나 하나가 청취자 "소유자"전에 제거되어 있는지 확인 (또는 소유자 소유자 ...)가 삭제되어 액세스 할 수 없게됩니다 (아래 사례 C). 이것은 사실상 모든 클래스에서 "free()"또는 "destroy()"메소드를 엄격하게 구현 (및 사용)하는 것을 의미합니다.
  • 소유자 클래스의 입력란 인 리스너 (예 : 리스너가 로컬 메소드 변수가 아니어야 함)에 대한 참조가 약합니다 (아래 사례 E).

당신은 다른 사람이 사용할 수 있도록하기위한 라이브러리를 작성하는 경우는, 청취자에 약 참조 아마 옵션되지 않기 때문에 어느 쪽도하지 컴파일 타임이나 런타임 자바 리스너가에 등록하는 것을 확인하는 어떤 방법이 있는가에 피사체는 다른 쪽 끝에 강력한 링크가 있습니다. 주위의 많은 논의와는 달리,이 문제는 청취자가 익명의 클래스인지 여부와 관련이 없습니다. 그것은 청취자가 "상대방"(피사체 측이 아니라 관찰자 측에있는 끝)에서 어떻게 연결되는지에 관한 문제입니다. - 아니 놀라움, 단순히 하나의 사실은 무엇을 기대의 확인 하루의 끝에서

:


여기 내 콘솔 출력됩니다. 청취자가 관찰자 모델의 어느 쪽인가에 강한 참조를 가지는 경우, 청취자는 가베지 컬 렉트되지 않습니다. 참조가 약한 경우 개체를 원하기 전에 가비지 수집 될 수 있습니다 (예 : notifiedCount가 0 인 경우 G 참조).

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 단계 : 청취자 고전 강한 참조로

  • 일 :
  • 나는 두 개의 서로 다른 청취자 지원 클래스를 생성
0 그 청취자에게 약한 참조와 1,234,
  • 다른 :
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 SubjectInterface { 

    public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener); 
    public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener); 
} 
: 차례로 인터페이스를 확장

public interface ListenerSupport extends SubjectInterface { 

    public void firePropertyChangeEvent(PropertyChangeEvent propertyChangeEvent); 
} 

2 단계 : 생성자를 통해 리스너 지원 유형을 제어 할 수있게 해주는 구체적 주제 클래스를 만들었고 인스턴스 생성이 지원되는 리스너를 사용하여 리스너에게 알릴 수있는 값을 변경할 수있었습니다. STEP 3

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); 
    } 
} 

: 메모리의 양 눈에 띄는을 차지하도록 난 대형 더블 배열 관찰 클래스를 만들었다. 또한 관찰 클래스에 두 개의 다른 청취자에게 서비스를 제공하는 기능을 부여했습니다. - 관찰자 자체의 인스턴스 생성시 관찰자가 청취자에 대해 강력한 참조를 생성하도록 만든 필드 수신기 및 -이를 반환하는 메서드 내에서 만든 리스너 (그래서 관찰자는 청자에 대한 언급이 없었다). 또한 알림을 추적하는 카운터를 만들었습니다. 4 단계

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; 
    } 
} 

: 마지막으로, 나는의 7 가지 "구성"사용해 내 주요 클래스를 만들었 : 리스너에

  • 강한/약한 참조를
  • 필드/메소드 로컬 청취자,
  • 청취자를 제외하고/사용하지 않음.

    이 시험판의 출력은 위에 나열되어 있습니다. 메모리를 계산하기 전에 가비지 콜렉터를 호출하여 실제로 볼 수있는 메모리가 실제로 사용되는지 또는 사용되는지 확인합니다. 그것 뿐이다

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(); 
    } 
} 

. 다른 사람들에게 유용 할 수 있기를 바랍니다. 그것은 나에게했다.