[C#] 와! 가비지 컬렉션 아시는구나?

WestCoast·2022년 5월 28일
0

C#

목록 보기
2/2

CLR과 가비지 컬렉션


  • CLR이 자동 메모리 관리(Automatic Memory Management)기능을 제공

  • 자동 메모리 관리 기능의 중심에 가비지 컬렉션이 있다.

  • 가비지 컬렉터도 소프트웨어이기 때문에 CPU와 메모리 같은 컴퓨팅 자원을 소모한다.

  • 기본적으로 C#으로 작성된 모든 코드는 관리형 코드(Managed Code)에 속한다. 누가 관리하냐고? 바로 CLR.
    비관리형 코드(Unmanaged Code)에는 C/C++ 등이 있다.

  • CLR은 실행되는 코드에 대해 메모리 할당, 보안, 스레딩, 가비지 컬렉팅 등의 일을 수행한다.

  • C#으로 비관리형 코드(Unmanaged Code)를 작성하기 위해서는 unsafe 키워드를 이용하면 된다. 이 경우에는 CLR이 제공하는 서비스를 받을 수 없다.


세대별 가비지 컬렉션


“버스의 출입구 쪽에 가까이 있는 승객일수록 버스에서 빨리 내릴 확률이 높고, 출입구로부터 멀리 있는 승객일수록 버스에서 최대한 오래 버틸 확률이 높다.”

  • CLR은 메모리를 0, 1, 2의 3개 세대로 나누고 0세대에는 빨리 사라질 것으로 예상되는 객체들을, 2세대에는 오랫동안 살아남을 것으로 예상되는 객체들을 위치시킨다. 1세대는 0세대와 2세대의 중간이다.

  • 기본적으로 메모리 할당은 0세대 힙에만 일어난다. 그리고 가비지 컬렉션이 일어날 때 0세대에서 해제되지 않는 객체들은 1세대로 승격된다. 그럼 1세대 가비지 컬렉션이 일어난다면? 0세대와 1세대 모두에 대해서 가비지 컬렉션을 수행하고 0세대에서 살아남은 객체는 1세대로, 1세대에서 살아남은 객체는 2세대로 승격된다. 2세대는 더 이상 말하지 않아도 알 것이라고 생각한다.

  • 참고로 2세대 가비지 컬렉션을 GC 2 혹은 풀 가비지 컬렉션(full garbage collection)이라고 하고 모든 세대에 대해 가비지 컬렉션이 수행되는 것이다.
    거기에 더해 LOH도 가비지 컬렉션 된다. LOH는 밑에서 설명한다.

  • 세대별로 가비지 컬렉션의 주기는 보통 0세대가 10회 일어날 때 1세대가 1회 일어나는 정도라고 한다. 마찬가지로 1세대가 10회 일어날 때 2세대가 1회 수준으로 일어나면 괜찮은 수준이라고 한다.


가비지 컬렉션의 수행 시점


  • 가비지 컬렉션의 정확한 수행 시점은 미래를 먼저 보고 오지 않는 이상은 정확하게 측정하기 어렵다고 한다. 하지만 MSDN에서 기본적으로 수행되는 시점을 제공해주고 있긴 하다.

가비지 수집 조건 – MSDN
가비지 수집은 다음 조건 중 하나가 충족될 경우 발생합니다.

  • 시스템의 실제 메모리가 부족합니다. 이는 OS의 메모리 부족 알림 또는 호스트에서 표시되는 메모리 부족을 통해 감지됩니다.
  • 관리되는 힙의 할당된 개체에 사용되는 메모리가 허용되는 임계값을 초과합니다. 이 임계값은 프로세스가 실행됨에 따라 계속 조정됩니다.
  • GC.Collect 메서드가 호출됩니다. 가비지 수집기가 지속적으로 실행되므로 이 메서드를 호출해야 하는 경우는 거의 없습니다. 이 메서드는 주로 특이한 상황 및 테스트에 사용됩니다.
  • 메모리가 허용하는 임계값에 대한 부가설명
    각 세대별 힙과 LOH는 각각 버짓(budget)이라고 불리는 한정된 공간을 가지고 있다. 이 버짓을 초과하는 메모리 할당이 발생하면 해당 세대의 가비지 컬렉션이 수행된다. 그리고 세대가 승격될 때 해당 세대의 버짓을 초과하는 객체가 들어온다면 이 때도 가비지 컬렉션이 수행된다.

LOH(Large Object Heap)


  • CLR의 가비지 컬렉션은 Managed Heap을 대상으로 일어난다. 사실 이 Managed Heap은 두 종류로 나눠져 있다. 하나는 SOH(Small Object Heap)이고 다른 하나는 LOH(Large Object Heap)이다. SOH는 지금까지 위에서 설명한 3세대로 이루어진 힙이다. 이 힙에는 85KB 이하의 객체들만 할당된다. 그래서 이름이 ‘작은 오브젝트 힙’이다.

  • LOH는 예상대로 85KB 이상의 크기를 갖는 객체들만 할당된다. 그리고 세대 따위는 없다. 왜 이렇게 구분이 되어있을까 묻는다면 마소의 최강 엔지니어들이 ‘85KB 이상은 따로 관리하는 것이 좋겠더라’ 라는 것이 쉬운 설명이다.

  • 좀 더 들여다보자면 LOH는 기본적으로 가비지 컬렉션이 일어나도 메모리 컴팩션(메모리 압축)은 일어나지 않는다. 그러니까 프로그램을 실행하고 시간이 지나면 메모리 단편화가 일어나고 LOH의 메모리는 중간중간 자유 메모리 공간이 송송 뚫려있는 모습일 것이다.
    왜 이렇게 해놨을까? 이유는 ‘크기가 큰 객체’라는 특성 때문이다. 크기가 큰 객체는 메모리 컴팩션을 할 때 작은 객체들에 비해 오버헤드(대부분 메모리를 0으로 초기화하는 시간) 역시 커진다. 이런 이유 때문에 메모리 단편화가 일어나는 문제를 없애기보다도 그냥 놔두는 편이 성능에 효율적이라고 판단한 것이다.

  • 이 힙은 세대가 구분되어 있지 않다. 그리고 2세대 가비지 컬렉션이 일어날 때만 LOH도 가비지 컬렉션이 수행된다.
    이유? 역시 당연히 있다. 그리고 역시 위에 설명했던 ‘크기가 큰 객체’라는 특성 때문이다. 보통 2세대 가비지 컬렉션은 0세대 가비지 컬렉션이 100번 수행되어야 1번정도 수행되는 꼴이다. 크기가 큰 객체는 할당하는 것도, 해제하는 것도 큰 일이다. 그래서 되도록 적은 주기로 가비지 컬렉션이 되도록 만든 것이다.


참고

profile
게임... 만들지 않겠는가..

0개의 댓글