어셈블리 동적 로딩의 문제점

  • Post author:
  • Post category:
  • Post comments:4 Comments
  • Post last modified:July 20, 2007

당분간은 C++/CLI에서 손 떼고 Visual C++에 전념할 듯 하다. 그 전에 마지막으로 그 동안 해왔던 일을 정리해볼까 한다.

어셈블리 동적 적재와 해제

일반 DLL와 달리 어셈블리는 마음대로 불러들였다가, 떼낼 수가 없다. System.Assembly에는 Load 메써드가 있지만 Unload 메써드는 없다. 어셈블리를 닷넷의 DLL 정도로 생각해왔다면, 이쯤에서 헉! 소리가 나온다. 그럼 어쩌라고?

우회할 방법은 있다. 우선 새로운 응용프로그램 도메인, 즉 AppDomain을 생성한다. 응용프로그램 도메인은 격리된 메모리 모델을 제공한다. 우선 .NET으로 작성된 응용프로그램을 실행시키면, 해당 프로세스 안에 기본 응용프로그램 도메인이 생성되고, 그 안에 진입점 Main을 포함한 코드가 들어간다. 만약 사용자가 AppDomain.CreateDomain() 메써드를 실행시키면, 하나의 프로세스 안에 두 개의 AppDomain이 존재하게 된다.

기본 응용프로그램 도메인을 제외한 다른 도메인은 프로세스 실행 중간에 Unload할 수가 있다. 그러니 이렇게 하면 동적 적재와 해제가 가능해진다. 우선 새 응용프로그램 도메인을 생성한다. 새로 생성된 AppDomain에 어셈블리를 적재한다. 어셈블리가 필요 없어졌거나 어셈블리를 교체해야 하면, 응용프로그램 도메인 전체를 내렸다가 다시 올린다.

자, 문제는 해결됐다. 정말 그럴까? 예상했겠지만 세상 일, 쉽지 않은 법이다.

동적 해제하려고 이 고생을?

별도의 응용프로그램 도메인을 생성하면 또 다른 문제가 발생한다. 격리된 메모리 모델이다 보니 평상시에 하듯 간단하게 객체를 생성하고 메써드를 호출할 수 없다. 외부 응용프로그램 도메인에 접근하려면 원격 또는 리모팅(Remoting)이라 불리는 기술을 써야 한다. 문제는 크게 두 가지이다. 우선 성능 저하가 있다는 점이다. MSDN 라이브러리를 보면 해당 응용프로그램 도메인이 같은 프로세스 안에 있다면, 다른 프로세스 안에 있을 때보다 최적화해 준다고 한다. 그러니 조금 봐주기로 한다.

두번째 문제는 진짜 심각하다. 개체를 원격화하려면, 즉 다른 응용프로그램 도메인에서 접근할 수 있는 개체를 만들려면 방법은 크게 세 가지이다. 값으로 마샬링되는 개체, 참조로 마샬링되는 개체, 컨텍스트 바인딩 개체가 가능하다. 문제는 참조 개체로 선언하려면 반드시 System.MarshalByRefObject를 상속받아야 한다는 사실이다. C# 등에선 단일 상속만 지원하기 때문에, System.MarshalByRefObject를 상속받고 나면 추가 상속을 받을 수 없다. 이를 회피하고자 System.MarshalByRefObject를 상속받는 클래스 A를 만들고, 원래 부모 클래스 격이었던 클래스 B가 클래스 A를 상속 받은 후에, 최종적으로 원격화할 최종 클래스가 클래스 B를 상속받는 구조를 시도해봤다. 물론 허사였다.

결국 원격화할 클래스는 Provider나 Factory 패턴을 취하게 된다. 이 문제를 회피하려고 별의별 방법을 생각해보고 있긴 하지만, 현재로선 답이 없다.

MS의 개발자들은 바보인가?

지금까지의 상황을 이해하면 이런 생각이 든다. 이런 바보들! 그냥 MarshalByRefObject 같은 클래스 말고 [Serializable] 같은 애트리뷰트만 제공했으면 얼마나 좋았겠어!

뭐, MS의 개발자들이 바보일 리는 없고 나름대로 이유가 있다. Inheriting from MarshalByRefObject에서 직접 답을 들어보자.

The reason has to do with performance. The CLR has a large number of optimizations which it can apply to objects that are guaranteed to be local. If the object is possibly remote, then these optimizations are invalidated. Examples include method inlining by the JIT, direct field access, fast instantiation in the local heap, direct method invocation for non-virtuals and more efficient type tests like cast operations.

When the benefit of these optimizations is considered, it completely outweighs the programming model impact to the inheritance hierarchy. This decision is key to achieving our long term goal of performance parity with native code.

역시나 성능 이슈 때문이다. 거봐라! 세상 일, 결코 쉽지 않은 법이다.

그래도 의문이 가시지 않는다면 MarshalByRefObject에 대한 좀더 상세한 논의를 살펴보자.

소박한 바램. 어떻게든 동적 적재와 해제 문제를 쉽게 해결할 방법 좀 마련해줘요~~~

Author Details
Kubernetes, DevSecOps, AWS, 클라우드 보안, 클라우드 비용관리, SaaS 의 활용과 내재화 등 소프트웨어 개발 전반에 도움이 필요하다면 도움을 요청하세요. 지인이라면 가볍게 도와드리겠습니다. 전문적인 도움이 필요하다면 저의 현업에 방해가 되지 않는 선에서 협의가능합니다.
0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
JInho Seo
17 years ago

KAISTZEN님, 흥미 있는 기사네요!! C/C++로 CLI/CLR 접근하려면 국내 개발자들이 그리 많치 않습니다. 아직 Visual Studio 6.0의 MFC 에서 Native Developing에 머물러 있는 사람이 많습니다. 혹시 Jeffery Richeter 의 CLR via C++ 이란 책을 읽어 보셨나요? Performance 도 문제고 사실 동적 로딩보다 언로딩 이슈가 큽니다. 가버지 콜렉션과의 상관 관계 때문에요
아무튼 흥미로운 기사입니다!!

최재훈
17 years ago

언로딩 때문에 죽을 맛이네요. 동적 로딩과 해제 문제만 해결되면 좀더 유연한 설계를 할 수 있어서, 다른 회피 수단을 좀더 찾아볼 생각입니다. ㅎㅎ

Jeffery Richeter 아저씨 책은 항상 읽을만 했는데, CLR via C++은 아직 읽지 못했습니다. 아직 기술 조사 수준이니 본격적인 개발에 들어가게 되면 한권 사야겠습니다.

kabbala
17 years ago

제 블로그 구독자님을 위한 이벤트가 있습니다. playin.innori.com/1867 참조.  암호는 1234 입니다.

최재훈
17 years ago

멋진 이벤트인데 막내동생도 이젠 중3이라 제가 참여하긴 힘드네요. ^^