프로그래밍 하면서 많은 데이터가 메모리에 저장됩니다.
하지만 더 이상 필요없는 데이터를 메모리에 계속 남길 필요가 없겠죠...
프로그램이 더 이상 필요하지 않은 메모리를 해제하지 않고 계속 가지고 있어서,
사용 가능한 메모리가 점점 줄어드는 현상을 메모리 누수라고 합니다.
이 메모리 누수를 예방하기 위해서 C나 C++의 경우 메모리를 수동으로 관리해야 합니다.
하지만 C#, Python, Java, Javascript 등 언어에는 가비지 컬렉터라는 기능으로 자동으로 관리합니다.
프로그램이 더 이상 사용하지 않는 메모리(객체, 변수 등)를 자동으로 정리해주는 기능으로
C, C++과 달리 프로그래머가 직접 메모리를 해제하지 않아도 되게 해주는 유용한 기능입니다.
IT에서 garbage의 의미가 좀 나누어지는데
데이터 분석가 입장에서는 분석과 아무 관련 없는 데이터를 말할 수도 있습니다만
여기서는 모종의 이유로 참조할 수 없게 된 객체들을 의미합니다.
객체를 생성할 때 힙 메모리 영역에 저장되는데
C#의 경우 힙은 세 가지 세대(Generation)로 나눌 수 있습니다
✅ 0세대
방금 만들어진새로운 개체가 들어가는 곳이며,
일시적인 변수가 여기 해당됩니다.
✅ 1세대
0세대와 2세대 사이의 버퍼라고 보시면 되고
0세대에서 살아남은 데이터들이 모이는 곳입니다.
✅ 2세대
가장 오랫동안 살아있는 객체입니다.
1세대 중 GC를 수행했을 때 살아남은 데이터들이 2세대로 불립니다.
데이터 중 많은 검문을 걸쳐 생존한최종 생존자라고 보시면 됩니다
가비지 컬렉터가 수행하는 경우는 보통은 3가지로 보는데
0세대에 자리가 없거나
시스템 메모리 자체가 부족하거나
특정 힙 메모리 사용량이 초과할 때 수행합니다.
즉, 메모리에 자리 없어지면 수행한다고 보시면 됩니다.
가비지 컬렉터의 수행과정은 크게 3가지로 정리할 수 있습니다.
- Mark
현재 사용하고 있는 객체들을 추적하는 과정입니다.
즉, 모든 객체를 찾고 거기서 도달 가능한 객체들을 추적(mark)합니다.
- Sweep
식별하는 단계입니다. 도달 불가능한 객체는 해체 대상이 되는거죠.
- Compact
남은 객체들을 연속된 공간으로 압축(compact)하는 과정입니다.
메모리 할당과 해제가 반복적으로 일어나면메모리 단편화가발생하는데
Compact 과정을 통해 비어있는 메모리 공간에 가져와서 사용하여 낭비되는 공간이 없게 만듭니다. 이로 인해할당 속도 향상됩니다.
앞에서 가비지 컬렉터 과정에서 메모리 단편화 현상을 언급했습니다.
메모리 단편화는 메모리에 빈 공간은 많은데,
연속된 큰 공간이 없어서 새로운 데이터를 넣지 못하는 현상입니다.
메모리 단편화는 2가지의 경우의 수로 나눌 수 있습니다.
| 종류 | 현상 |
|---|---|
| 외부 단편화 | 메모리 사이에 빈 공간이 흩어져 있어서, 연속된 큰 공간이 없는 경우 |
| 내부 단편화 | 할당받은 메모리 블록 안에 사용되지 못하는 빈 공간이 남는 경우 [■■■■■■■■■■□□□□□□] ← 총 16바이트 중 10바이트만 사용, 6바이트는 낭비됨 |
사실 가비지 컬렉터로 메모리 단편화를 어느 정도는 예방할 수는 있지만
메모리 할당과 해제가 계속 일어나면 성능저하가 생길 수 있습니다.
특히 게임 쪽은 할당과 해제가 밥 먹듯이 일어나기 때문에 GC 스파이크가 발생할 수 있습니다.
GC 스파이크는 GC가 수행될 때 프로그램이 잠깐 멈추는 현상인데
게임할 때 프레임 드랍이나 렉이 걸리는 현상이 일어날 수 있습니다.
흔히 많은 객체를 한 번에 생성과 삭제가 진행되면 일어납니다.
흔히 무쌍류 게임할 때 적이 많아질수록 끊기는 현상이 생기는 것이
GC가 열일하고 있어 생기는 현상입니다.
즉, 치울게 많아 정리하는 것이 버거워 잠시 멈추고 치우고를 반복하는 것입니다.