[C#] Memory Management (Releasing unmanaged resources, IDisposable interface)

jiho·2020년 1월 10일
0

C#

목록 보기
3/4

C++ 이후 나온 언어들 대부분 GC(Garbage Collector)에 의해 메모리 관리가 되기 때문에 학습에 비중을 안 뒀었습니다.
C# 관련 공부 중 메모리관련해서 한번 쯤 알아두면 좋을만한 내용을 정리하겠습니다.

Heap vs Stack

모리에는 stack memory와 heap memory 두 개의 카테고리가 있습니다. modern 운영체제의 발전과 함께, stack과 heap은 물리적 메모리나 가상메모리에서 어디에서나 있을 수 있게 되었습니다.

우선 간단히 말하자면, Stack memory는 더 빠르게 작동하지만 size가 제한되어있습니다.
(더 빠르게 동작하는 이유는 CPU에 의해 직접적으로 관리되고 FIFO 방식을 사용하기 때문에 그것은 L1, L2 cache에서 데이터를 가지고 있을 확률이 높기 때문이라고 합니다.)

반면에 힙 메모리는 stack에 비해 조금 더 느리지만 풍부한 자원이 있습니다. 아마 조금 느린 이유는 data를 힙에 할당할 때 빈 메모리를 찾고 저장해놓고 해당 위치에 대한 테이블에 저장하는 등 추가적인 오버헤드가 존재하기 때문입니다.

사실 Stack과 Heap 그 자체에 차이가 있다기보다는 주변 OS나 하드웨어적인 동작방식에 의해 속도가 차이나게 됩니다.

간단히 macOS terminal에서 unlimited -a 명령으로 stack size를 확인해보면 8,192KB이고 다른 memory는 "unlimited"이라는 것을 확인할 수 있습니다. stack size에 제한이 있는 이유는 stackoverflow문제 때문인 것 같습니다.

Struct vs Class

stack과 heap개념와 관련된 c#에서의 구조체와 Class의 차이점을 한번 알아보겠습니다.
object type을 생성할 때 우리가 사용할 수 있는 C# keywords는 두 개가 있습니다.
class 와 struct 입니다.
둘다 fields나 methods같이 같은 member를 질 수 있습니다. 둘의 차이는 어떻게 메모리가 할당되느냐에 있습니다.

우리가 class를 사용해서 type을 정의할 때, 우리는 reference type을 정의합니다. 이것은 object가 heap에 할당되고 오직 해당 object에 대한 메모리 주소만 stack에 저장됩니다.

우리가 struct를 사용해서 type을 정의할 때는 value type을 정의합니다. 이것은 object가 stack에 할당됩니다.

만약 struct가 struct 타입이 아닌 field type을 사용한다면 해당 필드는 heap에 저장될 것입니다. (해당 데이터는 stack과 heap 둘다에 저장됩니다.)

struct types:

  • Number: byte, sbyte, short, ushort, int, uint, long, ulong, float, double, and decimal
  • Miscellaneous: char and bool
  • System.Drawing: Color, Point, and Rectangle

그 이외의 대부분은 class type입니다. (string을 포함해서)

.NET Framework는 Reference Type과 Value Type을 정의합니다. C#은 struct와 enum키워드를 사용함으로써 custom value type을 정의할 수 있습니다. class, delegate와 interface도 만들어지는 것은 reference Type입니다.

.NET의 내부적인 메모리 layout에 대한 기술적 상세내용은 아래 링크를 참조하시면 좋을 것 같습니다.
https://adamsitnik.com/Value-Types-vs-Reference-Types/

Release unmanaged resource

생성자가 unmanaged resource를 할당했다고 상상해봅시다. .NET에 의해 제어되지않는 file or mutex under the control of operating system 와 같은 자원을 말합니다.
unmanaged resource는 직접 release해야합니다.

간단한 예제 코드를 보죠.

※ finalizer (also known as a destructor)?
.NET runtime에 의해 호출되는 method입니다. constructor와 같은 이름을 가지고 있습니다. 하지만 prefix로 tilde(~)가 붙게됩니다.

public class Animal{
  public Animal(){
    // allocate any unmanaged resources
  }
  ~Animal(){
  	// deallocate any unmanaged resources
  }
}

unmanaged rosource를 가지고 작업할 때, 위와 같이 finalizer를 정의해서 해당 resource를 직접 release해줘야합니다. 하지만 하나의 finallizer을 가지는 것에 대한 문제점은 .NET garbage collector가 해당 타입에 대해 할당된 resource들을 완벽히 해제하기 위해서 두 번의 garbage collection을 요구한다는 것입니다
ㄴ 왜 두번 호출되지? 그냥 내부적으로 두번 호출되는 것 같습니다. 알아봐야겠습니다.

해당 타입을 사용하는 개발자에게 명시적으로 해당 타입이 소유하고 있는 unmanageed resource를 해제할 수 있는 method를 제공하는 것을 추천드립니다. garbage collector가 즉각적이고 결정적으로 unmanaged resource를 해제하고 그 때 managed memory 영역을 한번의 collection으로 해제할 수 있게하기 위해서!

위와 같은 행동을 취하기위해서 표준적인 mechanism이 있습니다.
바로 IDisposable interface를 구현함으로 위와 같은 구현을 할 수 있습니다.

public class Animal: IDisposable{
  public Animal()
  {
    // allocate unmanaged resource
  }
  ~Animal()
  {
    if (disposed) return;
    Dispose(false);
  }
  bool disposed = false; // have resources been released?
  public void Dispose()
  {
    Dispose(true);
    //공용 언어 런타임에서 지정된 개체에 대해 종료자를 호출하지 않도록 요청합니다.
    GC.SuppressFinalize(this); 
    
  }
  protected virtual void Dispose(bool disposing){
    if(disposed) return;
    
    // deallocate the *unmanaged* resourece
    // ...
    
    if(disposing){
      // deallocate any other *managed* resources
      // ...
    }
    disposed = true;
  }
}

위 코드 패턴을 정리해보겠습니다.

사용자가 직접 해당 resource를 release할 수 있는 public Dispose method를 제공해주었구요. CLR(common language runtime)에 의해 release될 때를 위해서 finalizer도 구현해줬습니다. 그리고 IDisposable 인터페이스에 의해 구현해야하는 protected void Dispose(bool) 를 통해서 공통적으로 unmanaged resource를 release해주고 인자에 따라 managed resource를 release해주는 method를 구현해줍니다.(아마 finalizer에 의한 dispose호출은 자동으로 managed resource를 release해주기 때문에 위와 같이 분기한 것 같습니다.)

what is unmanageed resource??

https://stackoverflow.com/questions/3607213/what-is-meant-by-managed-vs-unmanaged-resources-in-net

unmanaged resource 가 정확히 어떤 것을 가리킬가요? 위 링크에 자세히 나와있지만
외부 자원들을 말합니다. 데이터베이스라던지 파일IO라던지 등등.
그에 반해 managed resource 는 .NET 에서 생성한 모든 객체를 말하는 것이죠!

https://docs.microsoft.com/ko-kr/dotnet/standard/garbage-collection/unmanaged

profile
Scratch, Under the hood, Initial version analysis

0개의 댓글