약한 참조 청취자가 청취자가 내 메모리 문제를 해결할 수 있습니까?Java - 청취자가 약한 참조 리스너를 지원하면 내 메모리 문제를 해결할 수 있습니까?
답변
나는 그것을 스스로 체크 아웃하기 위해 약간을했다. (아래 참조). 내 결론에 동의합니까?
소개 :
여기 관찰자 모델 (수신기 모델)의 맥락에서 강하고 약한 참조의 사용 용도 및 양을 보여주는 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 단계 : 청취자 고전 강한 참조로
- 일 : 나는 두 개의 서로 다른 청취자 지원 클래스를 생성을
- 다른 :
public class ListenerSupportWithWeakReferences implements ListenerSupport {
List<WeakReference<PropertyChangeListener>> weakListeners =
new ArrayList<WeakReference<PropertyChangeListener>>();
public void addPropertyChangeListener(
PropertyChangeListener propertyChangeListener) {
weakListeners.add(new WeakReference<PropertyChangeListener>(propertyChangeListener));
}
public void removePropertyChangeListener(
PropertyChangeListener propertyChangeListener) {
for(WeakReference<PropertyChangeListener> weakReference : weakListeners){
if(weakReference.get()==propertyChangeListener){
weakListeners.remove(weakReference);
break;
}
}
}
public void firePropertyChangeEvent(PropertyChangeEvent propertyChangeEvent) {
for(WeakReference<PropertyChangeListener> 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<ObservingObject> 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<ObservingObject>();
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();
}
}
. 다른 사람들에게 유용 할 수 있기를 바랍니다. 그것은 나에게했다.
질문/답변으로 작성해야합니다. – aioobe
StackOverflow는 블로그를 쓰는 지점이 아닙니다. – ceejayoz
http : //blog.stackoverflow.co.kr/2011/07/its-ok-to-ask-and-answer-your-yourself-questions/ – sp00m