Windows Vista Architecture


클릭하여 확대해 보시길… 첨언하지면, 그림의 “Longhorn”은 잘못된 표기입니다. Longhorn은 Window Vista의 서버버전의 제품명으로 의미변경 되었다고합니다. 꽤 오래전에 인터넷 상에서 받아 보던 것으로 자료가 옛날것이긴 하지만, 명칭 이외의 부분에 대해서는 거의 틀린 부분이 없기 때문에 Vista의 전체적인 아키텍쳐 구성 요소가 어떻게 되는지를 살펴보기에 용이할 것 같습니다.http://www.gisdeveloper.co.kr/?p=209&preview=true

C++/CLI의 Dispose Pattern에 대한 고찰

리소스 해제를 위한 .NET 개발환경에서 제공하는 Dispose 패턴을 파악하기 위해 테스트용으로 적용할 클래스 정의는 다음과 같으며 총 세가지의 경우로 시험을 해보았다.

ref class T : public IDisposable {
public:
    T() {
        Console::WriteLine(L"T() invoked");
    }

    ~T() {
        Console::WriteLine(L"~T() invoked");
    }

    !T() {
        Console::WriteLine(L"!T() invoked");
    }
};

첫번째 시험 코드는 마치 지역변수처럼 할당하는 경우이다. 하지만 절대 지역 변수가 아니라는 점.. CLR Heap에 할당된다.

int main(array ^args)
{
    T a;
    return 0;
}

실행 결과는 다음과 같다. 지역변수처럼 변수의 유효 Scope를 벗어나는 순간 소멸자가 호출되었다. 실제 IL에 의해 구현된 내부 흐름은 소멸자 호출이 아니라 IDisposable::Dispose 매서드의 호출이다. 즉, 소멸자가 IDisposable::Dispose의 재구현이다. 또한 내부적으로 GC에 의해 호출되어져야했을 Finalize 매서드는 호출되지 않게 조치된다. C#과는 다르게 Finalize가 호출되지 않도록 자동화되었다는 점이 매우 특이하다.

T() invoked
~T() invoked

두번째 시험 코드는 C++에서는 포인터 개념으로 생각되는, 즉 C++/CLI는 Handle 개념으로 CLR의 Heap에 객체를 생성하였고 delete를 호출하지 않은 경우이다.

int main(array ^args)
{
    T ^a = gcnew T();
    return 0;
}

실행 결과는 다음과 같다. Finalize에 해당하는 !T()가 호출되었다는 점에 유의하자.

T() invoked
!T() invoked

세번째 시험 코드는 두번째와 다르게 delete 연산자를 적용해 주었다.

int main(array ^args)
{
    T ^a = gcnew T();
    delete a;
    return 0;
}

실행 결과는 다음과 같다. Finalize가 아닌 Dispose가 호출되었다.

T() invoked
~T() invoked

여기서 얻을 수 있는 가장 중요한 한가지 결론은 ~T()에 해당하는 Dispose()와 !T()에 해당하는 Finalize()의 코드는 절대로 같이 호출되지 않는다는 점이다. C++/CLI의 사용자가 .NET의 참조형 변수를 지역변수처럼 사용하든지, gcnew에 의해 할당하여 사용하든지.. 또한 사용한 후 delete를 했든지, 하지 않았든지 간에 ~T()와 !T() 둘중에 하나는 반드시 실행되다는 점이다. ~T()는 기존의 C++ 개념으로써 호출되며 !T()는 .NET의 GC에 의해 호출된다. 즉, 리소스 해제를 위한 코드는 ~T()와 !T()에 똑 같이 중복적으로 와야한다고 생각한다.