태그 보관물: weak-references

weak-references

.Net에서 약한 참조를 언제 사용합니까? 못했지만 캐시에서 사용해야한다고 생각합니다. Jon Harrop 박사는

나는 개인적으로 .Net에서 WeakReference 유형을 사용해야하는 상황을 직접 보지 못했지만 캐시에서 사용해야한다고 생각합니다. Jon Harrop 박사는 질문 에 대한 답변 에서 캐시에서 WeakReferences를 사용하는 것에 대해 매우 좋은 사례를 제시 했습니다 .

또한 AS3 개발자가 메모리 공간을 절약하기 위해 약한 참조를 사용하는 것에 대해 이야기하지만 종종 의도 한 목표를 달성하지 않고도 복잡성을 추가하는 것으로 보이며 런타임 동작은 예측할 수 없습니다. 너무 많은 사람들이 단순히 그것을 포기하고 대신 메모리 사용을보다 신중하게 관리하고 코드를 최적화하여 메모리를 덜 사용합니다 (또는 더 많은 CPU 사이클과 더 작은 메모리 풋 프린트를 절충하십시오).

Jon Harrop 박사는 또한 .Net 약한 참조는 부드럽 지 않으며 gen0에는 약한 참조의 공격적인 컬렉션이 있다고 그의 대답에서 지적했습니다. MSDN 에 따르면 약한 참조가 길면 개체를 다시 만들 수 있습니다 but the state of the object remains unpredictable..

이러한 특성을 감안할 때 약한 참조가 유용 할 수있는 상황을 생각할 수 없으며 누군가 나를 밝게 할 수 있습니까?



답변

나는 실제로 개인적으로 나에게 일어난 다음 세 가지 실제 시나리오에서 약한 참조의 합법적 인 실제 적용을 발견했습니다.

애플리케이션 1 : 이벤트 핸들러

당신은 기업가입니다. 귀사는 WPF 용 스파크 라인 제어를 판매합니다 . 판매는 크지 만 지원 비용으로 인해 사망하고 있습니다. 스파크 라인으로 가득 찬 화면을 스크롤 할 때 너무 많은 고객이 CPU 호깅 및 메모리 누수에 대해 불평하고 있습니다. 문제는 앱에서 새로운 스파크 라인을 생성하지만 데이터 바인딩으로 인해 오래된 스파크 라인이 가비지 수집되는 것을 방해한다는 것입니다. 너 뭐하니?

데이터 바인딩만으로는 더 이상 컨트롤이 가비지 수집되는 것을 막지 않도록 데이터 바인딩과 컨트롤 사이에 약한 참조를 도입하십시오. 그런 다음 데이터 바인딩을 수집 할 때 데이터 바인딩을 해제하는 종료자를 컨트롤에 추가하십시오.

응용 프로그램 2 : 가변 그래프

당신은 다음 존 카맥입니다. Tim Sweeney의 게임을 Nintendo Wii처럼 보이게하는 계층 적 세분화 표면에 대한 독창적 인 새로운 그래프 기반 표현을 발명했습니다. 분명히 나는 그것이 어떻게 작동하는지 정확하게 말하지 않을 것입니다. 그러나 그것은 모두 정점의 이웃이에서 찾을 수있는이 가변 그래프의 중심에 있습니다 Dictionary<Vertex, SortedSet<Vertex>>. 플레이어가 돌아 다니면서 그래프의 토폴로지가 계속 변경됩니다. 단 하나의 문제점이 있습니다. 데이터 구조가 도달 할 수없는 하위 그래프를 흘리며이를 제거해야합니다. 그렇지 않으면 메모리가 누출됩니다. 운 좋게도 당신은 천재이므로 도달 할 수없는 하위 그래프를 찾고 수집하도록 특별히 설계된 알고리즘 클래스가 있다는 것을 알고 있습니다 : 가비지 수집기! 당신 은 그 주제에 관한 Richard Jones의 훌륭한 논문을 읽었습니다.그러나 그것은 임박한 마감일에 대해 당황하고 걱정하게합니다. 너 뭐하니?

간단히 Dictionary해시 테이블을 약한 해시 테이블 로 바꾸면 기존 GC를 피기 백하고 도달 할 수없는 하위 그래프를 자동으로 수집 할 수 있습니다! 페라리를 통해 잎으로 돌아온다.

응용 프로그램 3 : 나무 장식

당신은 키보드의 자전거 타는 방의 천장에 매달려 있습니다. 누군가가 당신을 발견하기 전에 약간의 빅 데이터를 탐색하는 데 60 초가 걸립니다. AST의 조각을 분석 한 후 GC에 의존하는 멋진 스트림 기반 파서를 준비했습니다. 그러나 각 AST에 추가 메타 데이터 Node가 필요하고이를 빠르게 사용해야 한다는 것을 알고 있습니다 . 너 뭐하니?

a Dictionary<Node, Metadata>를 사용하여 메타 데이터를 각 노드와 연결할 수 있지만,이를 지우지 않는 한 사전에서 이전 AST 노드에 대한 강력한 참조는 활성 상태를 유지하고 메모리를 누출시킵니다. 이 솔루션은 약한 해시 테이블 로, 키에 대한 약한 참조 만 유지하고 키에 도달 할 수 없을 때 가비지가 키-값 바인딩을 수집합니다. 그런 다음 AST 노드에 도달 할 수 없게되면 가비지 수집되고 해당 키-값 바인딩이 사전에서 제거되어 해당 메타 데이터에 도달 할 수 없으므로 수집됩니다. 그런 다음 메인 루프가 끝난 후해야 할 일은 에어 가드를 통해 위로 밀어 올려 경비원이 들어올 때와 같이 교체해야한다는 것을 기억하십시오.

실제로 나에게 일어난 이러한 세 가지 실제 응용 프로그램 에서 GC가 가능한 한 적극적으로 수집 하기를 원 했습니다. 이것이 합법적 인 응용 프로그램 인 이유입니다. 다른 사람들은 모두 잘못입니다.


답변

이러한 특성을 감안할 때 약한 참조가 유용 할 수있는 상황을 생각할 수 없으며 누군가 나를 밝게 할 수 있습니까?

Microsoft 문서 약한 이벤트 패턴 .

응용 프로그램에서, 이벤트 소스에 첨부 된 핸들러는 핸들러를 소스에 첨부 한 리스너 오브젝트와 함께 소멸되지 않을 수 있습니다. 이 상황은 메모리 누수로 이어질 수 있습니다. WPF (Windows Presentation Foundation)에는 특정 이벤트에 대한 전용 관리자 클래스를 제공하고 해당 이벤트에 대한 리스너에 인터페이스를 구현하여이 문제를 해결하는 데 사용할 수있는 디자인 패턴이 도입되었습니다. 이 디자인 패턴을 약한 이벤트 패턴이라고합니다.

약한 이벤트 패턴은이 메모리 누수 문제를 해결하도록 설계되었습니다. 약한 이벤트 패턴은 리스너가 이벤트를 등록해야 할 때마다 사용할 수 있지만 리스너는 등록을 취소 할시기를 명시 적으로 알지 못합니다. 약한 이벤트 패턴은 소스의 객체 수명이 리스너의 유용한 객체 수명을 초과 할 때마다 사용될 수 있습니다. 약한 이벤트 패턴을 사용하면 리스너가 리스너의 객체 수명 특성에 영향을주지 않고 이벤트를 등록하고 수신 할 수 있습니다. 실제로 소스의 암시 적 참조는 리스너가 가비지 콜렉션에 적합한 지 여부를 판별하지 않습니다. 참조는 약한 참조이므로 약한 이벤트 패턴 및 관련 API의 이름 지정. 리스너는 가비지 수집 또는 소멸 될 수 있으며 소스는 현재 파괴 된 오브젝트에 대한 수집 불가능한 핸들러 참조를 유지하지 않고 계속할 수 있습니다.


답변

이것을 먼저 꺼내서 다시 보자.

약한 참조는 개체에 탭을 유지하려고 할 때 유용하지만 개체가 수집되는 것을 막기 위해 관찰을 원하지는 않습니다.

처음부터 시작하겠습니다.

의도하지 않은 공격에 대해서는 사전에 사과하지만, 잠재 고객에게 절대 말할 수 없기 때문에 잠시 동안 “Dick and Jane”수준으로 돌아가겠습니다.

따라서 객체를 얻었을 때 X-인스턴스로 지정합시다 class Foo-그 자체로는 살 수 없습니다 (대부분은 사실입니다). “아무도 섬이 아니다”와 같은 방식으로, 개체가 섬으로 승격 될 수있는 방법은 몇 가지뿐입니다. 비록 CLR에서 말하는 것은 GC 루트라고합니다. 기본적으로 GC 루트가되거나 GC 루트에 대한 연결 / 참조 체인이 설정되어있는 것이 기본적으로 Foo x = new Foo()가비지 수집 여부를 결정합니다 .

힙이나 스택 워킹으로 GC 루트로 돌아갈 수 없다면 효과적으로 고아가되어 다음주기에 표시 / 수집 될 수 있습니다.

이 시점에서 끔찍하게 고안된 몇 가지 예를 살펴 보겠습니다.

먼저, 우리의 Foo:

public class Foo 
{
    private static volatile int _ref = 0;
    public event EventHandler FooEvent;
    public Foo()
    {
        _ref++;
        Console.WriteLine("I am #{0}", _ref);
    }
    ~Foo()
    {
        Console.WriteLine("#{0} dying!", _ref--);
    }
}

상당히 간단합니다-스레드 안전하지 않으므로 시도하지 마십시오. 그러나 활성 인스턴스 및 종료시 대략적인 “참조 수”를 유지합니다.

이제 보자 FooConsumer:

public class NastySingleton
{
    // Static member status is one way to "get promoted" to a GC root...
    private static NastySingleton _instance = new NastySingleton();
    public static NastySingleton Instance { get { return _instance;} }

    // testing out "Hard references"
    private Dictionary<Foo, int> _counter = new Dictionary<Foo,int>();
    // testing out "Weak references"
    private Dictionary<WeakReference, int> _weakCounter = new Dictionary<WeakReference,int>();

    // Creates a strong link to Foo instance
    public void ListenToThisFoo(Foo foo)
    {
        _counter[foo] = 0;
        foo.FooEvent += (o, e) => _counter[foo]++;
    }

    // Creates a weak link to Foo instance
    public void ListenToThisFooWeakly(Foo foo)
    {
        WeakReference fooRef = new WeakReference(foo);
        _weakCounter[fooRef] = 0;
        foo.FooEvent += (o, e) => _weakCounter[fooRef]++;
    }

    private void HandleEvent(object sender, EventArgs args, Foo originalfoo)
    {
        Console.WriteLine("Derp");
    }
}

따라서 우리는 이미 GC 루트 인 객체를 가지고 있습니다 (자체적으로 구체적으로 말하면이 응용 프로그램을 실행하는 앱 도메인에 직접 체인을 통해 루팅되지만 두 가지 방법이 있습니다) Foo인스턴스 에 래 칭하는 방법 -테스트 해 보겠습니다.

// Our foo
var f = new Foo();

// Create a "hard reference"
NastySingleton.Instance.ListenToThisFoo(f);

// Ok, we're done with this foo
f = null;

// Force collection of all orphaned objects
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

이제, 위에서 언급 한 객체 f가 “수집 가능” 할 것으로 예상 하십니까?

아니, 지금에 대한 참조를 잡고 또 다른 목적이 있기 때문에 – Dictionary점에서 Singleton정적 인스턴스입니다.

자, 약한 접근법을 시도해 봅시다.

f = new Foo();
NastySingleton.Instance.ListenToThisFooWeakly(f);

// Ok, we're done with this foo
f = null;

// Force collection of all orphaned objects
// This should collect # 2 - you'll see a "#2 dying"
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

이제, 한 Foo번에 한 번만 f언급했던 객체에 대한 “하드”참조가 더 이상 없기 때문에 수집 WeakReference할 수 있습니다.

좋은 사용 사례 :

  • 이벤트 처리기 (이 내용을 먼저 읽으십시오 : C #의 약한 이벤트 )

  • “재귀 참조”를 유발하는 상황이 있습니다 (예 : 객체 A는 객체 B를 의미하며 객체 A는 “메모리 누수”라고도 함). (편집 : derp, 물론 이것은 사실이 아님)

  • 객체 모음에 무언가를 “브로드 캐스트”하고 싶지만, 객체를 살아있는 것으로 유지하고 싶지는 않습니다. a List<WeakReference>는 쉽게 유지 관리 할 수 ​​있으며, 위치를 제거하여 정리할 수도 있습니다.ref.Target == null


답변

여기에 이미지 설명을 입력하십시오

사용자가 소프트웨어를 오랫동안 실행하는 것이 점점 더 많은 메모리를 사용하고 다시 시작할 때까지 느리게 느려지는 경향이 있음을 알면서도 실제로 추적하기 어려운 논리적 누수처럼? 난 아니야

사용자가 위의 응용 프로그램 리소스 제거 요청시 다음 Thing2과 같은 이벤트를 제대로 처리하지 못하면 어떻게되는지 고려하십시오 .

  1. 포인터
  2. 강력한 참조
  3. 약한 참조

… 그리고 이러한 실수 중 하나가 테스트 중에 잡히고 스텔스 전투기 버그처럼 레이더 아래에서 날지 않을 것입니다. 공유 소유권은 대부분 무의미한 아이디어입니다.


답변

좋은 효과를 내기 위해 사용 된 약한 참조의 매우 예시적인 예는 ConditionalWeakTable 이며, 이는 DLR에서 (다른 곳에서) 추가 “멤버”를 객체에 첨부하는 데 사용됩니다.

테이블이 개체를 살아있는 상태로 유지하고 싶지 않습니다. 이 개념은 약한 참조 없이는 작동 할 수 없었습니다.

그러나 약한 참조는 버전 1.1 이후 .NET의 일부이기 때문에 약한 참조에 대한 모든 용도는 언어에 추가 된 지 오래 된 것 같습니다. 그것은 당신이 추가하고 싶은 것 같아서 결정 론적 파괴의 부족이 언어 기능에 관한 한 코너로 돌아 가지 않을 것입니다.


답변

C #으로 캐시 계층을 구현 한 경우 데이터를 캐시에 약한 참조로 넣는 것이 훨씬 좋습니다. 캐시 계층 성능을 개선하는 데 도움이 될 수 있습니다.

접근 방식이 세션 구현에도 적용될 수 있다고 생각하십시오. 대부분의 경우 세션은 오래 지속되는 개체이므로 새로운 사용자를위한 메모리가없는 경우가있을 수 있습니다. 이 경우 다른 사용자 세션 객체를 삭제 한 다음 OutOfMemoryException을 발생시키는 것이 훨씬 좋습니다.

또한 응용 프로그램에 큰 객체 (일부 큰 조회 테이블 등)가있는 경우에는 거의 사용하지 않아야하며 이러한 객체를 다시 만드는 것은 비용이 많이 드는 절차가 아닙니다. 그런 다음 실제로 필요할 때 메모리를 비울 수있는 주 참조와 같은 것이 좋습니다.


답변