[C#] dotnet Memory 누수 체크

참고

가비지 수집 기본 사항


배경.

개발한 서비스가 Docker 환경에서 OOM (Out of Memory)가 발생하였다. Widnow에서도 메모리 누수가 일어나는지 확인하기 위하여 dotMemory를 이용하여 누수 체크를 하였다.


Visual Studio Dynamic Tool


dotMemory

  • 확인결과 GC가 주기적으로 메모리를 잘 지워주고 있다
  • 단서1. 2세대 메모리까지 쌓이지 않는 것으로 봐서 CG가 열일하고 있다.
  • 단서3. 그래프 위쪽에 요동 치는 부분을 보면 확인 가능하다.

가바지 콜렉터 수집 조건.

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

  • 시스템의 실제 메모리가 부족합니다. 이는 OS의 메모리 부족 알림 또는 호스트에서 표시되는 메모리 부족을 통해 감지됩니다.
  • 관리되는 힙의 할당된 개체에 사용되는 메모리가 허용되는 임계값을 초과합니다. 이 임계값은 프로세스가 실행됨에 따라 계속 조정됩니다.
  • GC.Collect 메서드가 호출됩니다. 가비지 수집기가 지속적으로 실행되므로 이 메서드를 호출해야 하는 경우는 거의 없습니다. 이 메서드는 주로 특이한 상황 및 테스트에 사용됩니다.

메모리 세대.

GC 알고리즘은 몇 가지 고려 사항을 기반으로 합니다.

  • 가비지 수집기는 관리되는 전체 힙보다 관리되는 일부 힙에서 더 빠르게 메모리를 압축할 수 있습니다.
  • 개체가 새로울수록 수명은 더 짧아지고 개체가 오래될수록 수명은 더 길어집니다.
  • 새로운 개체일수록 서로 연결되는 경향이 있어서 애플리케이션에서 거의 동시에 액세스됩니다.

가비지 수집기의 성능을 최적화하기 위해 관리되는 힙은 0세대, 1세대 및 2세대의 3개 세대로 나뉘어 있으므로 수명이 길고 수명이 짧은 개체를 별도로 처리할 수 있습니다. 가비지 수집기는 새 개체를 0세대에 저장합니다

. 애플리케이션 수명의 초기에 만들어져 수집 후에도 남아 있는 개체는 수준이 올라가 1세대와 2세대에 저장됩니다. 전체 힙보다 일부 관리되는 힙을 압축하는 것이 더 빠르기 때문에 가비지 수집기는 수집을 수행할 때마다 이 체계를 사용하여 전체 관리되는 힙에서 메모리를 해제하는 대신 특정 세대에서 메모리를 해제할 수 있습니다.

  • 0세대. 가장 젊은 세대이며 수명이 짧은 개체를 포함합니다. 수명이 짧은 개체의 예로는 임시 변수가 있습니다. 가비지 수집은 이 세대에서 가장 자주 발생합니다.

    새로 할당된 개체는 새로운 개체 세대를 구성하며 암시적으로 0세대 수집입니다. 그러나 대형 개체인 경우 ‘3세대’라고도 하는 LOH(대형 개체 힙)에서 사용됩니다. 3세대는 물리적 세대로, 논리적으로는 2세대의 일부로 수집됩니다.

    대부분의 개체는 0세대 가비지 수집에서 회수되며 다음 세대까지 남아 있지 않습니다.

    애플리케이션에서 새 개체를 만들려고 할 때 0세대가 가득 찬 경우, 가비지 수집기에서는 개체에 대한 주소 공간을 확보하려고 시도하는 컬렉션을 수행합니다. 가비지 수집기는 관리되는 힙에서 모든 개체를 검사하는 대신 먼저 세대 0에서 개체를 검사합니다. 0세대에만 컬렉션을 수행하더라도 애플리케이션에서 새 개체를 계속 만들 수 있는 충분한 메모리를 확보하기도 합니다.

  • 1세대. 이 세대는 수명이 짧은 개체를 포함하며 수명이 짧은 개체와 수명이 긴 개체 사이에서 버퍼 역할을 합니다.

    가비지 수집기에서는 0세대 컬렉션을 수행한 후 연결할 수 있는 개체의 메모리를 압축하고 1세대로 승격시킵니다. 수집 후에도 존재하는 개체는 수명이 긴 성향이 있기 때문에, 이런 개체를 상위 세대로 승격시키는 것이 당연합니다. 가비지 수집기에서 0세대 수집을 수행할 때마다 1세대와 2세대에서 개체를 다시 검사할 필요가 없습니다.

    예를 들어, 0세대에서 수집을 수행했음에도 불구하고 애플리케이션에서 새 개체를 성공적으로 만드는 데 필요한 충분한 메모리를 확보할 수 없는 경우, 가비지 수집기는 1세대에서 2세대의 순서로 수집을 수행할 수 있습니다. 또한, 수집이 완료된 후에 세대 1에 존재하는 개체를 세대 2로 승격시킵니다.

  • 2세대. 이 세대는 수명이 긴 개체를 포함합니다. 수명이 긴 개체의 예로는 프로세스의 기간 동안 유지되는 정적 데이터가 포함된 서버 애플리케이션의 개체가 있습니다. 수집이 완료된 후에 2세대에 존재하는 개체는 다음 수집에서 연결할 수 없는 개체로 결정될 때까지 2세대에 보관됩니다.


유지 및 승격

가비지 수집에서 회수되지 않는 개체는 남은 개체라고 하며 다음 세대로 승격됩니다.

  • 0세대 가비지 수집에서 남은 개체는 1세대로 승격됩니다.
  • 1세대 가비지 수집에서 남은 개체는 2세대로 승격됩니다.
  • 2세대 가비지 수집에서 남은 개체는 2세대에 남아 있습니다.

가비지 수집기는 한 세대의 잔존율이 높음을 탐지하면 해당 세대의 할당 임계값을 늘립니다. 다음 수집으로 상당한 크기의 회수된 메모리가 발생합니다. CLR은 가비지 수집을 지연하여 애플리케이션의 작업 집합이 너무 커지지 않도록 하는 것과 가비지 수집이 너무 자주 실행되지 않도록 하는 두 가지 우선 순위 사이에서 지속적으로 균형을 유지합니다.


관리되지 않는 리소스 (UnManaged Memory)

가비지 수집기는 사용자 애플리케이션에서 만들어지는 대부분의 개체에 대해 메모리 관리 작업을 자동으로 수행할 수 있습니다. 하지만 관리되지 않는 리소스의 경우는 명시적으로 정리할 필요가 있습니다. 가장 일반적인 형태의 관리되지 않는 리소스로는 파일 핸들, 창 핸들 또는 네트워크 연결 등의 운영 체제 리소스를 래핑하는 개체를 들 수 있습니다. 가비지 수집기에서는 관리되지 않는 리소스를 캡슐화하는 데 사용되는 관리되는 개체의 수명을 추적할 수 있지만, 리소스 정리 방법에 대한 구체적인 정보는 알 수 없습니다.

관리되지 않는 리소스를 캡슐화하는 개체를 정의하는 경우 공용 Dispose 메서드에서 관리되지 않는 리소스를 정리하는 데 필요한 코드를 제공하는 것이 좋습니다. 메서드를 Dispose 제공하면 개체 사용자가 개체를 완료할 때 리소스를 명시적으로 해제할 수 있습니다. 관리되지 않는 리소스를 캡슐화하는 개체를 사용하는 경우 필요에 따라 Dispose를 호출해야 합니다.

728x90

이 글을 공유하기

댓글

Designed by JB FACTORY