2016-06-20 13 views
0

IDisposable을 구현하는 클래스의 인스턴스가 두 개있는 경우 첫 번째 클래스의 관리되지 않는 필드에 두 번째 인스턴스가 할당되면 어떻게됩니까?IDisposable, 관리되지 않는 필드, 참조 형식 및 할당

예를 들어, 다음과 같은 간단한 클래스를 고려하십시오

public unsafe class Image : IDisposable 
{ 
    private float* pixelsBase; 

    private GCHandle pixelsHandle; 

    public Image(int width, int height) 
    { 
     this.Width = width; 
     this.Height = height; 

     // Assign the pointer and pixels. 
     this.Pixels = new float[width * height * 4]; 
     this.pixelsHandle = GCHandle.Alloc(this.Pixels, GCHandleType.Pinned); 
     this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); 
    } 

    public float[] Pixels {get; private set;} 

    ~ImageBase() 
    { 
     this.Dispose(false); 
    } 

    public void Dispose() 
    { 
     this.Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 

     if (disposing) 
     { 
      // Dispose of any managed resources here. 
     } 

     if (this.pixelsHandle.IsAllocated) 
     { 
      this.pixelsHandle.Free(); 
      this.pixelsBase = null; 
     } 
    } 
} 

다른 하나에 할당 된 Image 클래스의 인스턴스에 pixelsBase 필드에 어떤 일이 일어날 것이라고? 당신이 결코를 호출하지하고 있기 때문에

예컨대

var firstImage = new Image(100, 200); 
var secondImage; new Image(300, 300); 

firstImage = secondImage; 
+4

동일한 개체에 두 개의 * 참조가 있으므로 개체를 복사하지 않습니다. GC에 아무런 차이가 없으며 0 참조가 남아있을 때만 오브젝트를 수집/마무리합니다. 코드가 Dispose()를 호출하고 다른 곳의 다른 코드가 객체를 계속 사용하는 경우에만 문제가 발생합니다. 어딘가에 NRE에 폭탄을 던져 ObjectDisposedException을 던져 더 나은 진단을 내야한다. –

+0

제 질문을 분명히하기 위해 예제를 확장했습니다. Unmanged 리소스가 이미 할당 된 경우에는 어떻게됩니까? 메모리 누출로 끝나나요? –

+3

다시 극적인 일은 없습니다. 이제 사용자가 만든 첫 번째 Image 객체에 대한 참조가 남아 있지 않습니다. 다음 가비지 콜렉션이이를 파기합니다. 참조와 개체의 차이점에 대해 좋아하는 C# 언어 책을 참조하는 것이 좋습니다. –

답변

2

IDisposable, 당신의 질문과는 아무 상관이 없습니다. 중요한 것은 파이널 라이저입니다. IDisposable과 파이널 라이저가 관련되어 있지만 런타임은 IDisposable을 전혀 신경 쓰지 않습니다.

그래서 파이널 라이저를 사용하면 어떻게됩니까? 객체가 더 이상 참조되지 않으면 finalizer는 finalizer 큐에 놓이고, 어떤 프로세스가 종료되지 않는 한 잠시 후에 실행됩니다. 귀하의 경우에는 GCHandle가 릴리스되고 Pixels 바이트 배열도 수집 할 수 있습니다.

C#으로 C++을 작성하거나 C++로 C#을 이해하지 마십시오. 그것들은 비슷해 보이지만, 특히 메모리 관리 측면에서 매우 다릅니다. C# finalizers는 C++ 소멸자와 거의 공통점이 없습니다.

관리 객체를 오래 고정하지 않으려면 힙 압축이 제대로 작동하지 않아야합니다. 즉, Pixels 어레이가 LOH에 있지 않으면 메모리가 확보되지 않습니다. 컬렉션이 생성 될 때 회수 된 모든 고정 된 핸들 (고지 사항 : 이것은 현재 MS.NET 런타임의 구현 세부 사항이며 계약 상 .NET에서는 에도 가비지 수집기가 전혀 필요하지 않으며 finalizers는 보장되지 않습니다. 이제까지 달린다, 매우 더 적은 끝).

배열에 관리되지 않는 메모리를 할당하는 것이 이미 포인터를 처리하고있는 경우 더 좋은 방법 일 수 있습니다. 백업 저장소에 관리되는 배열을 실제로 사용하려면 관리되지 않는 포인터가 필요한 범위에서 fixed을 사용하면 전체 개체를 수명 기간 동안 고정 된 상태로 유지하는 것보다 나은 영역이 될 수 있습니다. 실제로 적어도 개체의 메모리를 해제하는 두 개의 컬렉션이 필요합니다. 처음에는 GCHandle.Free을 호출하고 두 번째는 실제로 더 이상 고정 된 메모리를 회수하지 않습니다. Dispose을 수동으로 호출하는 경우에도 컬렉션이 실제로 메모리를 회수 할 때까지 기다려야합니다.

+0

감사합니다. 내 접근 방식이 있지만 항상 개선을 찾고 있습니다.포인터를 사용하여 이미지 픽셀에 빠르게 액세스 할 수 있도록 배열을 고정합니다. 이것은 루프 안에서 빠르지 않아야합니다. 그래서'고정 된 '것은 나에게 어떤 호의도주지 않을 것입니다. 이것이 무엇인지 알고 싶다면 [ImageProcessor github repo에 대한 링크] (https://github.com/JimBobSquarePants/ImageProcessor)를 참조하십시오. –

+0

@JamesSouth 흠, 그래,하지만 주위에'고정 된'*을 사용할 수 없어? 코드 수행 방식을 확인하기 위해 성능 프로파일 링을 수행 했습니까? 항상 데이터에 대한 라이브 포인터를 항상 보관해야하는 경우 메모리를 관리하지 않는 것이 좋습니다. 메모리를 고정하면 라이브러리의 목표를 상당히 저해합니다 (하나의 호스팅 환경에서는 사용자를 죽일 수 있습니다) . 그리고 디버깅하지 않을 때 바운드 검사를 피하는 것은 좋은 생각이 아닙니다. 대신 빈번하지 않아도 검사를 덜어 줄 수있는 방법을 찾으십시오.하지만 항상 올바른 것입니다. 다시 말하면 "루프 외부"는 훌륭하게 작동합니다. – Luaan

+0

나는 벤치마킹을했는데 불행하게도 테스트를 찾을 수 없어서 20-25 % 정도의 개선이있었습니다. 다시 테스트해야합니다. 이론적으로 고정 된 구문을 루프 외부로 옮길 수는 있지만 인덱서를 사용하여 픽셀에 빠르게 액세스하고 API를 다루기가 힘듭니다. 나는 perf [here] (https://github.com/JimBobSquarePants/ImageProcessor/issues/347)에 대한 토론을 받고 싶습니다. –