본문 바로가기

프로그램&DB/C#

박준호님의 C# 강좌 - C# 객체 소멸과 Garbage Collection

  • 객체의 생성과 소멸

    • 객체의 생성

      • new 연산자를 사용하여 class의 instance인 객체(object)를 생성

      • 이 때 객체가 생성되는 곳이 Managed Heap

    • 객체의 소멸

      • C++ 같은 경우 소멸자(destructor)를 정의하여 메모리 관리(리소스 해제)가 가능

      • C# 에서는 Garbage Collector가 메모리를 관리

        • 소멸자가 존재하지만 언제 호출되는지 예측할 수 없다.

        • 때문에 소멸자는 사용하지 않을 것을 권장

      • 불가피하게 객체의 소멸 작업을 해주어야 하는 경우

        • DB, 파일 및 네트워크 연결과 같은 것은 Unmanaged Resource 리소스 해제 시

        • IDisposable interfaceDispose() 사용

 

  • Garbage Collector

    • garbage

      • new 연산자를 통해 객체를 생성(managed heap)하여 사용하다가 해제된 메모리 공간

    • garbage collector

      • garbage collector는 managed heap이 부족하다고 판단되는 시점에 불필요한 객체의 Finalize()를 호출하여 객체를 소멸시키는 작업을 수행

    • 강제적인 garbage collector 작동

      • System namespace의 GC Class에 있는 Collect() method를 호출하여 garbage collector를 강제적으로 작동 시킬 수 있다.

      • 시스템에 성능 저하를 유발할 수 있어 불가피한 상황이 아니라면 강제 호출은 피해야 한다.

        • 예) 대용량의 객체를 사용한 뒤 시스템 부하를 줄여야 하는 상황

 

  • Gabarge Collection과 Generations

    • 생성된지 오래된 객체와 최근에 생성된 객체를 비교할 때 오래된 객체가 더 오랫동안 heap 영역에 남을 가능성이 높다.

      • Main()에 생성된 객체와 다른 method에서 생성된 객체를 비교

    • 0세대 : 최근에 생성된 객체, 단 한번도 garbage collection에 mark된 적이 없는 것

    • 1세대 : garbage collection이 mark했으나 아직 살아있는 객체, 즉 heap에 여유공간이 많아 제거하지 않은 객체

    • 2세대 : garbage collection이 mark한 후 , 한번 체크하고 갔지만 아직 살아있는 객체

    • garbage collection이 일어나면

      • 먼저 0세대에 해당하는 객체들을 mark하고 리소스 해제, 남은 객체는 다음 세대로 바꿈

      • 0세대 객체를 해제한 뒤에도 heap이 부족하면 1세대 객체도 mark후 해제

      • 그래도 부족하면 2세대도 mark 후 해제

 

  • System.GC class member

    • Collect()

      • GC가 heap에 있는 객체에 대해 FInalize()를 호출하게 함

      • 또한 특정 세대의 객체를 해제

    • GetGeneration()

      • 객체가 현재 속해있는 세대를 리턴

    • MaxGeneration

      • 시스템에 있는 2세대(최종 세대)를 리턴

    • ReRegisterForFinalize()

      • 현재 객체가 Finalize()를 호출하지 못하도록 되어 종료될 수 없을 때 종료 가능(finalizable) 상태로 재 등록

    • SupperessFinalize()

      • 현재 객체의 Finalize()를 호출하지 못하도록 설정

    • GetTotalMemory()

      • heap 영역의 사용중인 전체 memory를 리턴(byte 단위)

 

  • 소멸자(destructor)

    • new 연산자를 사용하여 객체를 생성하면 managed heap이라는 영역에 할당한다. 그 뒤

    • .NET Runtime은 자동적으로 Finalize() method를 지원하는가(소멸자가 구현되었는가)를 체크

    • 지원할 경우, 해당 객체의 reference를 finalization queue에 저장

    • GC가 garbage collection을 수행할 때 이 queue에 있는 객체들부터 처리하기 위해 소멸자를 호출

    • 때문에 소멸자만 믿고 기다리는 것은 좋은 방법이 아님

      • 더욱이 lock과 관련있는 작업이라면...

    • 소멸자는 Compile되면 Finalize()로 변환된다

    • Finalize()는 명시적으로 호출할 수 없기 때문에 소멸자를 사용해야 한다.

 class AnyClass{

          ~AnyClass(){

                          // 정리작업 수행

          }

}

 

 

 

 protected override void Finalize(){

            try{

                        // 정리작업 수행

            }

            finally{

                        base.Finalize();

            }

}

 

 

  • IDisposable interface와 Dispose()

    • GC는 관리되는 객체가 더 이상 사용되지 않을 경우 해당 객체에 할당된 리소스를 자동으로 해제하지만 garbage collection이 언제 발생할 지 예측할 수 없다

    • DIspose()는 코드내에서 리소스를 해제하도록 명시하는 방법

    • 객체가 더 이상 사용되지 않는 시점에 바로 리소스가 해제됨

    • Dispose()는 해당 객체의 GC.SuppressFinalize 메소드를 호출해야 한다

      • GC가 자동으로 호출하는 Finalize()는 리소스를 많이 소모하기 때문에 Dispose()를 통해 개체가 이미 정리된 경우라면 GC에서 개체의 Finalize 메소르드를 호출할 필요가 없다.

      • 현재 finalization queue에 있는 경우(소멸자가 구현된 경우) GC.SupperessFinalize()는 GC에 의해 Finalize 메소드가 호출되는 것을 억제

    • DIspose()를 구현할 때

      • class에 포함된 모든 리소스는 물론,

      • 상속 받았다면 base class의 Dispose()도 호출하여

      • 해당 base class의 모든 리소스도 해제해 주도록 한다.

 

[첨부 : 소스 파일]

출처 : (주)인터데브 솔루션 개발 사업부 박준호님의 C# 동영상 강좌