본문 바로가기

ASP.NET

[C#] 쓰레기 수집가

C# 에는 아주 착한 일을 하는 쓰레기 수집가가 있다.

집 안이 지저분하면 쓰레기들만 쏙쏙 골라서 갖다 버려준다.

이 일을 CLR (Common Language Runtime) 이라는 곳에서 맡아서 하는데, CLR이 뭐냐?

공통 언어 런타임은 마이크로소프트 닷넷 이니셔티브의 가상 머신 구성 요소이다. 프로그램 코드를 위한 실행 환경을 정의하는 마이크로소프트의 공통 언어 기반 표준의 기능이다. 공통 언어 런타임은 공통 중간 언어라고 불리는 바이트코드의 형태를 실행한다.

라고 위키백과에서 그러는데 뭔 소리인지 모르겠고

자바에서 JVM 같은 거라고 한다.

사실 난 JVM도 정확하게 뭔지는 모른다.

다만 우리가 코드를 컴파일하면 그걸 OS가 알아먹을 수 있게 바꿔주는 역할? 이라고 하는데

아무튼 CLR은 C#에서 그런 역할을 하는 것 같다.

 

하여간 얘가 쓰레기를 참 잘 비워준다.

어떻게 비우는지 알아보자.

 

그 전에 C# 에서 메모리 관리가 어떻게 되는지 알아야 하는데,

그걸 또 알려면 C# 의 타입에 대해서도 알아야 한다.

 

C#의 타입은  크게 두 종류로 나뉜다.

스택에 데이터를 저장하는 Value Type,

에 저장하는 Reference Type이 있다.

Value Type에는 bool, byte, int, float, char, enum, struct 등이 있고,

Reference Type에는 string, array, class 등이 있다.

 

Value Type은 선언 시에 스택에 저장됐다가 코드블록이 끝나면 알아서 사라진다.

근데 Reference Type은 우리가 직접 메모리를 해제 해주지 않는 이상 사라지지 않는다.

이걸 쓰레기라고 부른다.

 

이제 쓰레기를 어떻게 치우는지 알아보자.

어떤 객체가 메모리에 할당되면 그 객체의 위치를 참조하는 root 라는 친구가 있다.

쉽게 코드로보면

if (true)
{
    Object obj = new OBJ();
}

 

이런 코드가 있으면 obj가 만들어지는 순간

 

 

이렇게 된다.

그러다가 if 코드 블럭이 끝나게 되면

 

 

이렇게 OBJ를 가리키던 obj는 사라진다.

그럼 OBJ라는 쓰레기 하나가 탄생한다.

 

저 OBJ가 쓰레기라는걸 판단해서 메모리에서 갖다 버려야 하는데,

저게 쓰레기라는걸 어떻게 알까

 

방법은 쉽다.

root를 쭉 순회한다.

순회하고 나서 한 번도 참조 당하지 못한 heap 내의 객체들은 쓰레기라는 증거다.

그럼 바로 갖다 버린다.

 

끝.

 

알아본 김에 세대별 메모리 관리? 라는 것도 좀 알아보자.

오래 살아남은 객체들은 장수 객체로 인정해주고 메모리 제거 대상을 검사할 때 최대한 예외 시켜주는 제도다.

자바에서도 Eden, Survivor1, Survivor2, Old 뭐 이런 명칭들을 쓰면서 비슷한 제도를 갖고 있는데 이런게 C# 에도 있다.

C# 에서는 간단하게 0세대, 1세대, 2세대로 나눈다.

 

 

이렇게 메모리에 A, B, C, D 라는 객체가 쌓여 있다고 가정해보자.

처음 생성된 객체는 0세대로 들어간다.

0세대에 할당된 크기가 꽉 차게 되면 이제 비워준다.

 

 

예시로 B, C 가 지워진다고 해보면 한 번 쓰레기를 비운 이후에는

 

 

0세대에서 살아남은 친구들이 1세대로 승급을 한다.

 

그 뒤로 다시 객체들이 쌓인다.

 

 

이번엔 E, H가 사라진다고 가정해보면

 

 

이렇게 된다.

여기서 또 객체들이 생성되고 메모리가 꽉찬다.

 

 

이번엔 K를 지워보자.

 

 

지우고 I, J, L 을 1세대로 올리려는데 자리가 없다.

그럼 1세대에서도 한 번 쓰레기 검사를 쭉 돌리고 버릴건 버려줘야 한다.

G가 버려질 친구라고 가정해보자.

 

 

어느정도 예상 했겠지만 1세대에서 살아남은 친구들은 이제 2세대로 승급하며 고인물 객체가 된다.

 

만약 2세대까지 자리가 꽉차게 되면 그땐 Full GC 라고해서 메모리 전체를 검사하고 쓰레기를 비운다.

이 과정은 시간이 꽤 오래 걸리기 때문에 이때 잠시 프로그램이 멈춘다.

 

그래서!

이제 정리를 해보자면

0세대 > 1세대 > 2세대 순으로 쓰레기 정리가 자주 일어난다.

그리고 객체를 많이 할당할 수록 메모리가 금방 차고 Full GC를 실행할 확률이 높아진다.

그러면? 객체 생성을 막 함부로 하면 안되겠지.

 

아 물론~ 그렇다고 해서 만들어야할 객체까지 안만들면서 문제 해결하려고 머리 싸매고 있지는 말고

만들어야 한다면 만들지만 불필요한 객체까지 막 만들지는 말자~