마이 플밍 블로그

[C#] GC(Garbage Collector)의 작동원리 본문

Code/C#

[C#] GC(Garbage Collector)의 작동원리

레옹 2022. 4. 3. 01:44

GC란?

다들 알다시피 C#CC++과 달리 직접 메모리 해제를 할 필요가 없다.

이렇게 할 수 있는 이유로는 Garbage Collector라는 것이 있기 때문인데

이것은 CLR(Common Language Runtime, 공용 언어 런타임)에서 자동메모리 관리기능을 한다.

GC덕에 우리는 메모리 관리 부담을 덜하면서 개발할 수 있다.


 

1. CLR의 메모리 할당

C#에서의 할당을 보자면 소스코드 컴파일 후 실행하면 CLR에선 일정 크기의 메모리를 확보하게된다.

이렇게 관리되는 Heap 메모리 영역을 Managed Heap라고 한다.

 

이렇게 확보한 Managed Heap의 첫번째 주소에 다음 객체를 할당할 메모리 주소를 메모리의 포인터로 가리킨다.

 

객체를 생성하게 된다면 포인터가 가리키고 있는 주소에 객체를 할당하고 포인터는 해당 객체의 주소 바로 뒤로 이동하게 된다.

 

그 뒤로 다른 객체를 생성해도 위와 마찬가지로 포인터가 가리키고 있는 위치에 객체를 생성시키고 포인터는 해당 객체 주소 바로뒤로 이동한다.

이런 방식은 메모리 공간을 찾기위해 탐색할 필요도 없이 포인터의 위치에 바로 할당만 해주면 되기에 효율적이다.

 

2. CLR의 메모리 해제

메모리 해제 방식은 다음과 같다.

1. 현재 진행중인 쓰레드를 모두 중단한 후 GC 쓰레드 활성화

2. 루트 생성

3. 메모리에 있는 모든 것을 쓰레기로 간주

4. GC에서 루트를 참조해서 관계있는 데이터는 쓰레기에서 제외

5. 쓰레기를 제거하고 메모리 컴팩션(Memory Compaction)을 함

 

※루트 : 힙에 할당된 메모리의 주소를 참조하는 모든 객체들, 스택, 힙 두 영역에 모두 존재가능

※메모리 컴팩션 : 동적으로 시스템을 구성하는 기억 장소에서, 여러 곳에 흩어져 있는 가용 공간을 하나의 연속된 공간으로 만드는 작업

쓰레기 제거후 메모리 컴팩션


 

 

세대별 GC

CLR에선 빨리 해제될 객체나중에 해제될 것으로 예상하는 객체를 따로 관리한다.

CLR은 메모리를 0,1,2  3개의 세대로 나누고 0이 제일 빨리 사라질 것이라고 예상되는 것들

2는 마지막에 사라질 것으로 예상되는 것들로 모은다.

 

객체의 수명을 정하는 방식은 GC를 몇번을 거쳐갔는지에 따라 결정된다.

GC를 많이 거칠수록 세대가 높아지는 것이다.

 

GC 수행과정

1. 새로 생성된 객체들을 0세대 GC에 할당한다. 0세대 GC의 수용치가 한계에 도달할 시

가비지 컬렉션을 실행한다.

2. 이 과정에서 여러번 살아남은 0세대 GC에 존재하는 데이터들은 1세대로 옮겨진다.

마찬가지로 1세대 GC 수용치가 한계에 도달하면 0 세대 GC와 함께 가비지 컬렉션 실행

3. 1세대에서 여러번 살아남은 데이터는 2세대로 옮겨진다.

한계에 도달하면 0세대, 1세대와 함께 가비지 컬렉션을 실행한다.

 

가비지 컬렉션 수행 빈도는 0세대 > 1세대 > 2세대 이다.

2세대 힙까지 가득차게 된다면 더이상 여분의 공간이 없는 GC는 애플리케이션 실행을 멈추고

전체 가비지 컬렉션(Full GC)을 수행한다.


 

 

객체 크기별 GC

CLR에선 메모리를 2가지 정도로 나눠 관리한다.

1. 대형 객체 힙(LOH:Large Object Heap)

85KB 이상의 대형 객체

 

2. 소형 객체 힙(SOH:Small Object Heap)

85KB 미만의 소형 객체

 

큰 객체를 소형 객체 힙에 할당을 한다면 0세대 GC의 공간이 빠르게 찰테고 그러면

가비지 컬렉션이 자주 실행될 것이다. 이를 방지하기위해 큰 객체는 대형 객체 힙에 넣는 것이다.

 

하지만 단점도 있다. 대형 객체 힙은 소형 객체힙과 작동 방식이 다른데

소형 객체 힙은 객체를 할당할 포인터위치에 객체를 할당하지만 대형 객체 힙은 객체의

크기를 계산한 후 힙에 그 크기 만큼의 여유 공간이 있는지 탐색하고 할당한다.

 

가비지 컬렉션 수행후 소형 객체 힙의 메모리 공간은 잘 쌓은 책처럼 중간 중간 빈 공간들이

없도록 메모리 공간을 정리하지만 대형 객체 힙은 그대로 둔다.

만약 소형 객체 힙처럼 한다면 큰 용량의 메모리를 복사해야 하는데 이는 비용이 너무 크기

때문에 그냥 두는 것이다. 그래서 대형 객체 힙은 중간 중간에 빈 공간이 있다.

 

또한 CLR은 LOH를 2세대 GC로 보기 때문에 LOH의 객체를 제거하기

위해선 2세대 가비지 컬렉션이 수행되어야 한다.

위에서 말했듯 2세대 가비지 컬렉션은 애플리케이션이 멈추게 된다.

 

'Code > C#' 카테고리의 다른 글

C# 확장메서드 (Extension Method)  (0) 2021.09.28
Regular Expression  (0) 2021.09.27