Program Tip

WeakReference로 청취자의 장단점

programtip 2020. 11. 1. 18:35
반응형

WeakReference로 청취자의 장단점


청취자를 WeakReference로 유지하는 장단점은 무엇입니까?

물론 큰 '프로'는 다음과 같습니다.

WeakReference로 리스너를 추가하면 리스너가 스스로를 '제거'할 필요가 없습니다.

최신 정보

객체에 대한 유일한 참조가있는 리스너에 대해 걱정하는 사람들을 위해 addListener () 및 addWeakRefListener ()라는 두 가지 메서드가있을 수없는 이유는 무엇입니까?

제거에 신경 쓰지 않는 사람들은 후자를 사용할 수 있습니다.


우선, 리스너 목록에서 WeakReference를 사용하면 객체에 다른 의미 를 부여한 다음 하드 참조를 사용합니다. 하드 참조의 경우 addListener (...)는 " removeListener (..)를 사용 하여 명시 적으로 중지 할 때까지 특정 이벤트에 대해 제공된 객체에 알림"을 ​​의미 하고, 약한 참조의 경우 "특정 이벤트에 대해 제공된 객체에 알림 ( s) 다른 사람이이 객체를 사용하지 않을 때까지 (또는 removeListener로 명시 적으로 중지) ". 많은 상황에서 객체를 가지고 일부 이벤트를 듣고 GC에서 유지하는 다른 참조가없는 것은 완벽하게 합법적입니다. 로거가 예가 될 수 있습니다.

보시다시피 WeakReference를 사용하면 한 가지 문제를 해결할뿐만 아니라 ( "추가 된 리스너를 어딘가에서 제거하는 것을 잊지 않도록 명심해야합니다.") 또 다른 문제가 발생합니다. "내 리스너가 언제라도 청취를 중단 할 수 있음을 명심해야합니다. 더 이상 참조가없는 순간 ". 당신은 문제를 해결하지 않고 단지 하나의 문제를 다른 문제와 교환합니다. 이봐, 어떤 방식으로 어떤 식 으로든 - 당신은 명확 청취자 여러분의 설계 및 추적 livespan, 정의를 강요했습니다.

그래서 개인적으로 청취자 목록에서 WeakReference를 사용하는 것이 솔루션 이라기보다 해킹과 더 비슷하다는 언급에 동의합니다. 예를 들어, 레거시 코드가 잘 작동하도록 만드는 것은 알아 두어야 할 패턴이며 때로는 도움이 될 수 있습니다. 그러나 그것은 선택의 패턴이 아닙니다 :)

추신 또한 WeakReference가 추가 수준의 간접 지정을 도입하여 경우에 따라 매우 높은 이벤트 속도로 인해 성능이 저하 될 수 있다는 점도 주목해야합니다.


이것은 완전한 대답은 아니지만, 당신이 인용하는 바로 그 강점은 그것의 주요 약점 일 수도 있습니다. 액션 리스너가 약하게 구현되면 어떤 일이 발생할지 고려하십시오.

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

해당 액션 리스너는 언제든지 가비지 수집을받을 것입니다! 익명 클래스에 대한 유일한 참조가이를 추가하는 이벤트 인 것은 드문 일이 아닙니다.


리스너가 제대로 등록 해제되지 않은 수많은 코드를 보았습니다. 이것은 그들이 불필요한 작업을 수행하기 위해 여전히 불필요하게 호출되었음을 의미합니다.

하나의 클래스 만 리스너에 의존하는 경우 정리하기 쉽지만 25 개의 클래스가 이에 의존하면 어떻게 될까요? 제대로 등록을 취소하는 것이 훨씬 까다로워집니다. 사실, 코드는 리스너를 참조하는 하나의 객체로 시작하여 동일한 리스너를 참조하는 25 개의 객체가있는 미래 버전으로 끝날 수 있습니다.

사용하지 않는 것은 WeakReference불필요한 메모리와 CPU를 소모하는 큰 위험을 감수하는 것과 같습니다. 더 복잡하고 까다 롭고 복잡한 코드에서 하드 참조에 대한 더 많은 작업이 필요합니다.

WeakReferences자동으로 정리되기 때문에 전문가로 가득 차 있습니다. 유일한 단점은 코드의 다른 곳에 하드 참조를 유지하는 것을 잊지 말아야한다는 것입니다. 일반적으로 이는이 리스너에 의존하는 객체에서 발생합니다.

일단 등록되면 더 이상 이러한 리스너를 등록 해제 할 수 없기 때문에 (Kirk Woll에서 언급했듯이) 리스너의 익명 클래스 인스턴스를 만드는 코드가 싫습니다. 당신은 그들에 대한 언급이 없습니다. IMHO 코딩은 정말 나쁩니다.

null더 이상 필요하지 않을 때 리스너에 대한 참조를 사용할 수도 있습니다 . 더 이상 걱정할 필요가 없습니다.


정말 프로가 없습니다. weakrefrence는 일반적으로 가비지 수집을 방지하지 않으려는 캐시와 같은 "선택적"데이터에 사용됩니다. 청취자 가비지 수집을 원하지 않고 계속 청취하기를 원합니다.

최신 정보:

좋아, 나는 당신이 무엇을 얻고 있는지 알아 냈을 것입니다. 수명이 긴 객체에 수명이 짧은 리스너를 추가하는 경우 weakReference를 사용하면 이점이있을 수 있습니다. 예를 들어, 지속적으로 다시 생성되는 GUI의 상태를 업데이트하기 위해 PropertyChangeListeners를 도메인 개체에 추가하는 경우 도메인 개체는 GUI를 유지하여 빌드 될 수 있습니다. PropertyChangeListener를 통해 Employee 개체에 대한 리스너 참조를 통해 지속적으로 다시 생성되는 큰 팝업 대화 상자를 생각해보십시오. 내가 틀렸다면 정정하십시오. 그러나 전체 PropertyChangeListener 패턴이 더 이상 인기가 없다고 생각합니다.

반면에 GUI 요소 사이의 리스너에 대해 이야기하거나 GUI 요소를 수신하는 도메인 객체를 갖는 경우 GUI가 사라지면 리스너도 마찬가지이므로 아무것도 구매하지 않을 것입니다.

다음은 몇 가지 흥미로운 읽기입니다.

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

스윙 리스너 메모리 누수를 해결하는 방법은 무엇입니까?


솔직히 말해서 나는 그 아이디어와 당신이 addWeakListener로 무엇을하려고하는지 정확히 구매하지 않습니다. 나뿐일지도 모르지만 잘못된 좋은 생각 인 것 같습니다. 처음에는 유혹적이지만 암시 할 수있는 문제는 무시할 수없는 수준입니다.

weakReference를 사용하면 리스너 자체가 더 이상 참조되지 않을 때 리스너가 더 이상 호출되지 않을지 확신 할 수 없습니다. 가비지 수집기는 몇 밀리 초 후에 또는 전혀 메모리를 해제 할 수 있습니다. 이것은 리스너가 호출되지 않기 때문에 CPU를 계속 소비하고 예외를 던지는 것처럼 이상하게 만들 수 있음을 의미합니다.

스윙이있는 예는 UI 구성 요소가 실제로 활성 창에 연결된 경우에만 할 수있는 작업을 시도하는 것입니다. 이로 인해 예외가 발생할 수 있으며 알림이 충돌하도록 만들고 유효한 리스너가 알림을받지 못하게 할 수 있습니다.

이미 언급 한 두 번째 문제는 익명의 청취자입니다. 너무 빨리 해제 될 수 있습니다. 알림을 전혀받지 않거나 몇 번만 알림을받을 수 있습니다.

알림 수신을 중지하면 더 이상 제어 할 수 없으므로 달성하려는 것은 위험합니다. 그들은 영원히 지속되거나 너무 빨리 멈출 수 있습니다.


WeakReference 리스너를 추가하기 때문에 사용자 정의 Observable 객체를 사용하고 있다고 가정합니다.

다음과 같은 상황에서 개체에 WeakReference를 사용하는 것이 좋습니다. -Observable 객체에는 리스너 목록이 있습니다. -당신은 이미 다른 곳에서 청취자들에 대한 확실한 언급을 가지고 있습니다. (당신은 이것을 확실히해야 할 것입니다.)-Observable에 참조가 있다고해서 가비지 수집기가 리스너 지우기를 멈추는 것을 원하지 않습니다. -가비지 수집 중에 리스너가 정리됩니다. 리스너에게 알리는 메서드에서 알림 목록에서 WeakReference 객체를 지 웁니다.


사용 사례가 다음 GC주기 이후에 명시 적으로 존재해서는 안되는 리스너를 포함하지 않는 한 리스너에 WeakReferences를 사용하는 합법적 인 사용 사례를 생각할 수 없습니다 (물론 해당 사용 사례는 VM / 플랫폼에 따라 다름).

리스너가 선택 사항이지만 많은 힙을 차지하고 여유 힙 크기가 불안정 해지기 시작할 때 가장 먼저 가야하는 SoftReferences에 대해 좀 더 합법적 인 사용 사례를 상상할 수 있습니다. 일종의 선택적 캐싱 또는 다른 유형의 지원 청취자가 후보가 될 수 있다고 생각합니다. 그럼에도 불구하고 청취자와 청취자 간의 링크가 아닌 청취자의 내부가 SoftReference를 활용하기를 원하는 것처럼 보입니다.

일반적으로 영구 리스너 패턴을 사용하는 경우 리스너는 선택 사항이 아니므로이 질문을하는 것은 아키텍처를 재고해야하는 증상 일 수 있습니다.

이것은 학문적 질문입니까, 아니면 해결하려는 실제 상황이 있습니까? 실제 상황이라면 그것이 무엇인지 듣고 싶습니다. 해결 방법에 대한 더 많은, 덜 추상적 인 조언을 얻을 수 있습니다.


제 생각에는 대부분의 경우 좋은 생각입니다. 리스너 해제를 담당하는 코드는 등록 된 동일한 위치에 있습니다.

실제로 나는 청취자를 영원히 유지하는 많은 소프트웨어를 봅니다. 종종 프로그래머는 등록을 취소해야한다는 사실조차 알지 못합니다.

일반적으로 등록 취소시기를 조작 할 수있는 리스너에 대한 참조가있는 사용자 정의 객체를 반환 할 수 있습니다. 예를 들면 :

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

이 코드는 리스너를 등록하고 리스너를 캡슐화하고 인스턴스 매개 변수를 키로 사용하여 리스너를 정적 weakHashMap에 추가하는 keepFor 메소드를 갖는 객체를 반환합니다. 이는 적어도 someInstance 및 otherInstance가 가비지 수집되지 않는 한 리스너가 등록되도록 보장합니다.

keepForever () 또는 keepUntilCalled (5) 또는 keepUntil (DateTime.now (). plusSeconds (5)) 또는 unregisterNow ()와 같은 다른 메소드가있을 수 있습니다.

기본값은 영구적으로 유지 될 수 있습니다 (등록되지 않을 때까지).

이것은 또한 약한 참조없이 구현 될 수 있지만 리스너 제거를 트리거하는 가상 참조입니다.

편집 :이 aproach https://github.com/creichlin/struwwel 의 기본 버전을 구현하는 작은 lib를 만들었습니다.


원본 포스터에 대한 3 가지 제안이 있습니다. 오래된 스레드를 부활 시켜서 죄송하지만이 스레드에서 이전에 내 솔루션에 대해 논의하지 않은 것 같습니다.

먼저 JavaFX 라이브러리에서 javafx.beans.values.WeakChangeListener의 예를 살펴보십시오.

둘째, Observable의 addListener 메소드를 수정하여 JavaFX 패턴을 업그레이드했습니다. 새로운 addListener () 메서드는 이제 나를 위해 해당 WeakXxxListener 클래스의 인스턴스를 생성합니다.

"fire event"메소드는 XxxWeakListeners를 역 참조하고 WeakReference.get ()이 null을 반환 할 때 제거하도록 쉽게 수정되었습니다.

전체 목록을 반복해야하므로 remove 메서드가 좀 더 복잡해졌습니다. 즉, 동기화를 수행해야합니다.

셋째,이 전략을 구현하기 전에 유용 할 수있는 다른 방법을 사용했습니다. (하드 참조) 리스너는 아직 사용 중인지 여부를 확인한 새로운 이벤트를 받았습니다. 그렇지 않은 경우 GC가 허용되는 옵저버의 구독을 취소합니다. 수명이 긴 Observable을 구독하는 수명이 짧은 리스너의 경우 노후화를 감지하는 것은 상당히 쉬웠습니다.

"항상 리스너의 구독을 취소하는 것이 좋은 프로그래밍 관행이었습니다. 리스너가 스스로 구독을 취소 할 때마다 저는 로그 항목을 만들고 나중에 코드의 문제를 수정했습니다."라고 규정 한 사람들을 존중합니다.


WeakListeners are useful in situations where you specifically want GC to control the lifetime of the listener.

As stated before, this really is different semantics, compared to the usual addListener/removeListener case, but it is valid in some scenarios.

For example, consider a very large tree, which is sparse - some levels of nodes are not explicitly defined, but can be inferred from parent nodes further up the hierarchy. The implicitly defined nodes listen to those parent nodes that are defined so they keep their implied/inherited value up to date. But, the tree is huge - we don't want implied nodes to be around forever - just as long as they are used by the calling code, plus perhaps a LRU cache of a few seconds to avoid churning the same values over and over.

Here, the weak listener makes it possible for child nodes to listen to parents while also having their lifetime decided by reachability/caching so the structure doesn't maintain all the implied nodes in memory.


You may also need to implement your listener with a WeakReference if you are unregistering it somewhere that isn't guaranteed to be called every time.

I seem to recall we had some problems with one of our custom PropertyChangeSupport listeners that was used inside row Views in our ListView. We couldn't find a nice and reliable way to unregister those listeners, so using a WeakReference listener seemed the cleanest solution.


It appears from a test program that anonymous ActionListeners will not prevent an object from being garbage collected:

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

produces:

start collect
end collect
first finalized
second finalized
end program

참고URL : https://stackoverflow.com/questions/6337760/pros-and-cons-of-listeners-as-weakreferences

반응형