C# GC(Garbage Collector) 가비지 컬렉터

2022. 8. 17. 20:53C#/C#에 대한 다양한 공부

[GC(Garbage Collector)]

C#에서 객체 생성 시 메모리 할당 방식

->  영역부터 차곡차곡..

-> 메모리 할당이 가능한 영역을 찾는 과정이 없기 때문에 속도가 빠름

 

*C#에서 GC에 의한 메모리 해제 방식

GC Root 라는 것이 있다. 루트는 힙에 있는 객체를 가리키는 참조를 말한다.

-> 여기서 a 루트

 

  • 루트 목록을 순회하면서 루트가 참조하는  객체와 관계를 조사한다. 어떤 루트와도 관계가 없다면 쓰레기로 간주한다

  • 쓰레기 객체가 차지하는 메모리는 해제한다.
  • 남은 객체들은 다시 비어있는 공간에 재배치 시켜준다.

 과정이 GC  이다. C# 메모리 관리를 용이하게 하기 위해 세대별 GC 지원한다.  이유는 아래와 같다.

  • GC 파편화를 방지하기 위해 메모리를 압축하는데 한번에 전체를 대상으로 하기보다 일부분만 실행하는게  빠르다.
  • 최근에 만들어진 객체일 수록 수명이 짧고 오래될 수록 수명이 길다고 판단하고 분류한다.

가장 처음 할당된 메모리들은 0세대고 GC 한번 이루어 질때마다 한세대씩 증가한다. 0세대와 1세대 GC 발생했음에도 메모리가 부족하면 2세대 GC 발동한다.

 

다양한 GC

GC 동작원리와 C#에서의 세대별 GC까지 알아보았다. 사실 C#에서 힙은 여러 종류의 힙이 존재한다. 특히 CLR 객체의 크기 따라 메모리를 크게 두가지로 나눠서 관리하는데 이를 SOH(Small Object Heap), LOH(Large Object Heap)으로 구분한다.

  • 용량이 작은 객체 (85KB 미만) : SOH
  • 용량이  객체 : (85KB 이상) : LOH

SOH 경우 세대별 GC 진행하고 LOH 경우 방식이 조금 다르다.

  • SOH GC 발동하면 메모리를 재배치하지만 LOH 해제된 공간은 그대로 둔다. 메모리 사이즈가 큰 LOH의 경우 재배치하는 것이 오히려 오버헤드가 크다.
  • SOH 3가지 세대로 나눠서 관리하지만 LOH 2세대밖에 존재하지 않는다. 2세대 GC가 일어날때만 해제시키고 이때 많은 오버헤드가 발생한다. LOH때문이라도2세대 GC가일어나지않도록주의해서객체를생성해야한다.

메모리 단편화 때문에 용량이  객체는 너무 자주 생성하고 해제하고를 반복하면 안된다. 최악의 경우 메모리 할당에 실패하는 경우가 발생한다. 그래서  메모리를 자주 할당하는 것은 주의가  필요하다

 

*이러한 GC들은 그래서 언제 발생하는가?

  • 시스템의 물리적 메모리가 부족할 
  • GC.Collect 메서드가 호출될 
  • 메모리가 미리 설정된 임계값을 초과할 

이중에서 메모리가 미리 설정된 임계값을 초과할 때에 대해 좀더 설명을 해보겠다.

0세대, 1세대, 2세대, LOH 각각 버짓(budget)이라 불리는 할당 임계치 가지고 있다.  버짓을 초과하는 메모리 할당이 발생하면 해당 세대의 GC 발생하는 것이다.

-> 버짓은 고정된 값이 아니라 메모리 상황에 따라서, GC 수행됨에 따라서 지속적으로 변화되는 값이다.

 

*GC가 왜 자주 발생하면 안좋은지

일반적으로 GC 주기가 길면  수록 좋다. GC 발생하지 않는다는 말은 그만큼 메모리 할당, 회수가 적다는 얘기이고 할당된 객체들이 0세대에서 1세대, 2세대로 나이를 먹는 시간이 오래걸린다는 얘기다. 하지만 GC 자주 발생하면 그만큼 객체들이 빠르게 늙어버려 2세대 힙에 들어갈테고 2세대 힙의 크기가 크다면 2세대 GC 발생하는데 이는 너무  자원을 소모한다.