CS Interview

C#에서의 GC(Garbage Collector), 가비지 컬렉터

게임이 더 좋아 2021. 6. 5. 22:07
반응형
728x170

 

C#에서는 닷넷프레임워크가 제공하는 자동메모리관리를 이용한다.

 

**C/C++에서는 Memory leak가 쉽게 일어남.

 

닷넷프레임워크에서는 Managed heap 이나 Stack에다 메모리를 할당한다.

아래 그림을 살짝 보자.

 

1

 

 

 

2

 


 

위에서 Managed heap과 Stack을 쓴다고 했다.

value type은 스택에 할당되고, LIFO

reference type은 힙에 할당된다

 

**해당 범위(scope)를 벗어나면 스택에 있는 값은 자동 소멸된다.

**하나라도 참조하고 있으면 힙의 값은 소멸되지 않는다.

 

 

 

CLR(공통언어런타임, Common Language Runtime)이 어플리케이션을 위한 메모리 공간을 확보한다.

아래와 같이 힙을 확보한다. (reference 타입)

 

 

?? 스택에 저장되는 것은 뭔데??

오브젝트를 선언할 때 오브젝트는 힙에 할당되지만

오브젝트를 가리키는 reference 값이 스택에 저장되는 것이다.

 

 

오브젝트를 선언하면 아래와 같이 순서대로 할당되는 것이다.

 

 


?? 그렇다면 GC는 언제 하느냐??

 

크게 3가지 경우가 있다.

 

객체를 할당하여 할당하는 임계치가 넘어갈 때 (각 세대 별)

GC.Collect 메서드를 호출할 때

시스템의 메모리가 부족할 때

 

 

 


 

GC Root라는 것이 있다.

루트는 위에 2개의 그림을 보면 알 수 있다.

루트는 힙에 있는 객체를 가리키는 참조를 말한다. 

if(true)
{
	object a = new object();
}

여기서 바로 a가 루트라고 할 수 있다.

루트는 스택이나 힙(static)에 생성된다.

 

.NET 어플리케이션을 실행하면, JIT 컴파일러가 루트 목록을 생성하고

CLR이 루트목록을 돌면서 상태를 갱신하는 것이다. (GC가 참조함)

 

 

 

가비지 컬렉터는 

루트 목록을 순회하면서 루트가 참조하는 힙 객체와 관계를 조사한다.

-> 어떤 힙과도 루트와 관계가 없다면 필요 없는 Garbage

-> 다른 힙 객체를 참조한다면 Not Garbage

쓰레기 객체가 있던 메모리는 비워줘야 한다.(Sweap)

 

이 때문에 이러한 방법의 특징은

Heap 전체를 검사할 필요가 없어 C/C++보다 빠르다.

메모리 할당시 메모리 조각 현상이 발생하지 않는다.

 

저 부분을 비워주고 땡겨준다.(Compaction)

루트 목록에 대한 조사 이후 Garbage Collector가 힙을 순회하며 비어있는 공간에

인접 객체들을 이동시켜준다.

 

 

 


 

CLR은 메모리를 효율적으로 관리하기 위해 세대를 나눈다 0,1,2이다.

 

**세대가 낮을수록, 0에 가까울수록 메모리에서 빨리 사라진다고 생각함.(많이 쓰이지 않았다)

 

 

0세대는 GC가 아직 적용하지 않은 객체

1세대는 0세대와 2세대 사이

2세대는 GC를 2번 이상하고도 힙에 저장되어 있는 객체

**가비지 컬렉션이 실행되면 0->1->2 순으로 세대가 바뀌거나 사라지거나 할 것이다.

 

 

0세대,1세대,2세대 힙을 나누어 관리한다.

 

2세대 힙이 오브젝트로 가득차게 된다면(Full GC)

CLR이 해당 어플리케이션을 일시 중단한 뒤 모든 세대에 대해서 다시 GC를 실행한다.

**어플리케이션이 차지하는 메모리가 크면 중지시간 길어짐.

 

 

**중지가 일반적이지만 멀티스레드로 어플리케이션 수행에 최소한으로 영향을 끼치면서 GC를 돌리기도 한다.

 

 


 

GC는 자동으로 이루어지기 때문에 우리가 할 것은 많지 않다.

 

우리는 객체를 너무 많이 할당하려고 하면 위와 같이 Full GC 상태에 도달할 것을 안다.

때문에 객체를 너무 많이 할당하면 안된다.

 

또한 큰 객제 할당을 한다면 LOH(대형 객체 힙, Large Object Heap)는 2세대 힙으로 간주되고

대형 객체 힙은 메모리 공간 효율이 좋지 않아.

더욱 빠르게 GC를 요구하는 상황이 온다.

 

또한 참조되는 count가 많을 수록 가비지 컬렉션에 객체가 살아남을 확률이 높다.

참조 관계가 많은 객체는 살아남았을 때

GC이후 살아남은 객체의 세대들의 위치를 옮기기 위해서

객체의 각 필드 간 참조 관계를 모두 조사, 참조하는 메모리 주소 모두를 수정해야 하기 때문에 오버헤드 발생

 

 

루트 목록이 작을수록 GC 횟수가 줄어서 가비지 컬렉션을 빨리 끝내게 된다.

 

 

작은 구멍이 댐을 무너뜨린다는 말이 있듯이..

하나가 모여 큰 에러를 만든다.

외우고 살 것은 아니지만 일을 다 끝내고 한 번쯤 생각해보자.

 

 

 


참고링크

이것이 C#이다 - 한빛미디어

https://www.slideshare.net/Kamal1997/gc-in-c

반응형
그리드형