간단하게 말해봐.Net의 약한 참조

6483 단어 .net

약인용은 무엇입니까?


약인용이 무엇인지 알아내려면, 강인용이 무엇인지 먼저 알아야 한다.강인용은 결코 심오한 개념이 아니라, 사실 우리가 평소에 사용하는 것이다.Net 참조는 강력한 참조입니다.예를 들면 다음과 같습니다.
Cat kitty = new Cat();

변수 kitty는 무더기의Cat 대상 실례를 가리키는 강력한 인용입니다.CLR의 쓰레기 회수 메커니즘은 강제로 인용된 모든 대상을 표시하고, 나머지 표시되지 않은 대상은 쓰레기로 회수된다는 것을 우리는 모두 알고 있다.대상이 계속 어떤 강력한 인용에 의해 지향된다면 쓰레기로 회수되지 않는다는 얘기다.
이런 점에서 약인용은 완전히 다르다. 어떤 대상이 약인용에 의해 지향되더라도 그 대상은 쓰레기로 회수된다.약한 인용이 대상의 생명주기에 영향을 주지 않는다는 것이다.
System.Weak Reference 클래스는.net에서 제공하는 약한 인용의 실현은 다음과 같다.
WeakReference weakReference = new WeakReference(new Cat());

Cat strongReference = weakReference.Target as Cat;

if (strongReference != null)

{

    // Cat , strongReference 

}

else

{

    // Cat 

}

만약에 상례의 첫 번째 줄 코드와 두 번째 줄 코드 이전에 CLR에 쓰레기 회수가 발생했다면 그 Cat 대상의 실례가 이미 존재하지 않는다고 기본적으로 단정할 수 있다. 이때weakReference.Target은null입니다.
WeakReference 유형에는
Public WeakReference(Object target, bool trackResurrection)

여기서 bool 유형의 매개변수 trackResurrection은 이 WeakReference 인스턴스가 긴 참조인지 짧은 참조인지를 지정합니다.짧은 인용의 경우, 그 대상이 쓰레기 회수 메커니즘에 '불가달' 상태 (회수될 것) 로 표시될 때, 약한 인용의 Target 속성은null입니다.긴 인용과 약한 인용에 대해 대상을 가리키는 분석 함수가 호출된 후에도 Target 속성은 유효합니다.

약한 인용의 내부 실현


약한 인용은 정상적인 쓰레기 회수 메커니즘을 능가하는 것처럼 신기해 보이는데, 도대체 어떻게 이루어진 것일까?사실 WeakReference 유형은 내부에 GCHandle이라는 struct 유형을 봉인했는데, 바로 이 GCHandle이 약한 인용을 가능하게 하는 것이다.
CLR의 각 AppDomain에는 GC 핸들 테이블이 있습니다.이 테이블의 각 항목에는 두 가지 정보가 기록되어 있는데, 하나는 무더기 속의 어떤 대상을 가리키는 지침이고, 다른 하나는 이 테이블의 유형이다.총 4가지 표 항목 유형이 있는데, 그 중에서 Weak과 WeakTrackResurrection 두 가지 유형은 우리가 오늘 토론한 약한 인용과 관련이 있다.GCHandle 클래스는 GC 핸들 테이블을 조작하는 방법을 제공합니다.Alloc 방법을 사용하여 GC 핸들 테이블에 지정된 유형의 테이블 항목을 추가할 수 있습니다.쓰레기 수거가 시작되면 쓰레기 수거기는 가능한 모든 대상을 찾을 수 있다. (쉽게 말하면 유용한 대상이다.)그리고 GC 핸들 테이블에 있는 모든 Weak 형식의 항목을 훑어보며, 특정한 항목이 가리키는 대상이 실행 가능한 대상에 속하지 않는 것을 발견하면, 이 항목의 대상 바늘을null로 설정합니다.이어서 쓰레기 수거기는 도달할 수 없는 모든 대상에서 분석 함수를 정의한 대상을 찾아내고freachable라고 불리는 대기열에 넣는다. (freachable의 대상은 CLR의 특정한 라인을 기다려서 그들의 종결 함수를 호출한다.)이freachable의 대상들이freachable 대기열에 인용되었기 때문에, 그것들은 다시 실행 가능한 대상이 되었다.이 때 스팸 수거기는 GC 핸들 테이블에 있는 모든 WeakTrackResurrection 형식의 항목을 옮겨다니며, 아까와 같이, 특정한 항목이 가리키는 대상이 실행 가능한 대상에 속하지 않으면, 이 항목의 대상 바늘을null로 설정합니다.처음에 도달할 수 없다고 판정되고 분석 함수를 정의한 대상에 대해 GC 핸들 테이블에서 시계 항목의 바늘은null이 아니라는 것을 주의해야 한다.이것이 바로 Weak과 WeakTrackResurrection 두 가지 유형의 차이이다.
WeakReference는 GC 핸들 테이블 항목을 나타내는 GCHandle 객체를 통해 객체의 라이프 사이클을 추적하는 기능을 수행합니다.너도 단약 인용은 위크 유형의 GC 핸들 항목을 이용했고 장약 인용은 위크 트랙 Resurrection 유형의 항목을 이용했다는 것을 알 수 있을 것이다.

WeakReference 고려 사항


우선 WeakReference 자체도 분석 함수를 실현했다.더 이상 사용되지 않아도 즉각 회수되지 않고 메모리에서 더 오래 살 수 있다는 것이다.
또한 위에서 말한 바와 같이 WeakReference는 GC 핸들 테이블에 테이블 항목을 추가합니다.매번 쓰레기를 회수할 때마다 GC 핸들 테이블은 한 번씩 옮겨다닌다.만약 시스템에 대량의 Weak Reference가 존재한다면 GC 핸들 시계도 매우 방대하여 쓰레기 회수 효율을 떨어뜨릴 가능성이 높다는 것을 알 수 있다.
Weak Reference는 캐시와 자주 연결되지만, 대형 캐시 메커니즘을 실현하는 데 적합하지 않다.왜 그런 걸까요?한편, 앞에서 말한 바와 같이 Weak Reference 자체는 분석 함수를 실현했고 쓰레기 회수의 효율을 떨어뜨릴 수 있기 때문에 메모리에 대량의 Weak Reference 대상을 만드는 실례를 피해야 한다.다른 한편, 우리는 대상이 어떤 강력한 인용에도 지향되지 않고 약한 인용에만 지향된다면 쓰레기 수거를 한 번도 살 수 없을 가능성이 높다는 것을 안다.그래서 이런 방식을 통해 이루어진 캐시 메커니즘은 반드시 매우 짧은 캐시 정책을 가지고 있을 것이다. 이런 정책은 대부분의 상황에서 네가 기대하는 것이 아니다.

WeakReference의 세 가지 사용 장면


개체 캐시
이런 장면을 생각해 보자. 나는 메모리가 제한된 프로그램이 하나 있는데 이 프로그램에서 많은 메모리를 차지하는 비트맵 대상을 자주 사용하는데 다행히 이 비트맵 대상을 만드는 것은 복잡하지 않다.그래서 나는 그 비트맵 대상을 사용할 때마다 그것을 다시 만들고 사용한 후에 그것을 버린다. (인용을 보류하지 않는다.)
이런 방식은 나의 수요를 충분히 만족시킬 수 있지만, 더 이상 최적화할 수 있겠는가?분석을 해 보면 내가 비트맵 대상을 사용해야 할 때 내가 지난번에 사용한 비트맵 대상은 내가 버렸지만 쓰레기로 회수되지 않고 메모리에 남아 있을지도 모른다는 것을 알 수 있다.이때 만약 내가 이 비트맵 대상을 직접 사용할 수 있다면, 비트맵 대상을 재건하는 데 낭비되는 메모리와 CPU 자원을 절약할 수 있을 것이다.
개선 조치는 간단하다. 비트맵 대상을 사용한 후 직접 버리는 것이 아니라 약한 인용으로 가리키는 것이다.다음에 비트맵 대상에 접근할 때, 약한 인용을 통해 비트맵 대상이 메모리에 있는지 아닌지를 판단할 수 있으며, 만약 남아 있다면 직접 사용하고 그렇지 않으면 다시 만들 수 있다.
보조 디버깅
때때로 어떤 유형에 대해 우리는 현재 프로그램에 얼마나 많은 대상 실례가 존재하고 존재하는 실례가 어떤 실례인지 알아야 성능 분석을 할 수 있다.이때 우리는 약한 인용을 사용할 수 있다.예를 들어 다음 코드는 다음과 같습니다.
public class A

{

    private static List<WeakReference> _instances = new List<WeakReference>();



    public A()

    {

        _instances.Add(new WeakReference(this));

    } 



    public static int GetInstanceCount()

    {

        GC.Collect();

        return _instances.Count(x => x.Target != null);

    }

}

GetInstanceCount 메서드는 메모리에 있는 A 유형의 인스턴스 개수를 가져옵니다.또한 instances 집합을 통해 메모리에 있는 A 유형의 실례가 어떤 것들이 있는지 검사할 수 있다.메모리 유출 문제를 디버깅할 때 이 정보들은 모두 쓸모가 있다.
이러한 접근 방식은 다양한 성능 분석 Profiler 도구보다 가볍고 편리합니다. 
약한 사건
.Net의 이벤트는 때때로 메모리 유출 문제를 일으킬 수 있습니다.예를 들어 A가 B의 어떤 사건을 등록하면 이때 B는 A의 강력한 인용을 암암리에 보류하여 A가 B가 회수되거나 A가 B의 사건을 반등록할 때까지 메모리에 회수되지 못하게 한다.예를 들어 주 창의 Loaded 이벤트를 등록한 대상이 있습니다. 이 이벤트를 반등록하지 않으면 주 창은 주 창이 닫힐 때까지 이 대상을 인용합니다. 이 대상은 회수됩니다.그래서 우리가 어떤 대상의 사건을 등록할 때마다 무심코 메모리 유출의 위험을 묻을 수 있다.
이 문제를 해결하는 근본적인 방법은 필요할 때 사건의 반등록을 하는 것이다.그러나 어떤 경우 우리는 이'필요할 때'를 판정하기 어려울 수도 있다.또한 우리가 라이브러리의 공급자일 때 라이브러리의 사용자들이 반등록 사건을 기억하는 것을 보장하기 어렵다.따라서 또 다른 해결 방안은 약한 사건을 사용하는 것이다.
약한 사건의 실현 원리는 매우 간단하다. 바로 사건을 한 층 봉인하는 것이다.사건 발표자가 감청자를 직접 인용하지 않고 감청자의 약한 인용을 보류하도록 한다.이벤트가 터치되면 게시자는 감청자가 메모리에 존재하는지 확인하고 존재하면 알립니다.이렇게 되면 감청자의 생명주기는 발표자에게 의존하지 않게 된다.

좋은 웹페이지 즐겨찾기