
지금까지 메모리 관리에 대해 여러가지 포스팅을 작성하면서
메모리를 malloc 이나 mmap 시스템 콜을 써서 할당하고
free를 사용해서 꼭 해제해 줘야 한다는 것을 배웠습니다.
C언어를 사용할 때에는 더 이상 필요하지 않는 메모리를 free해주는 것은 우리의 책무예요.
안해주면 진짜 서운함.
사실 서운한 건 아니고, 에러입니다. 할당한 블록을 회수해주지 않는건 에러예요.
이 코드를 한번 볼까요?
void garbage()
{
int *p = (int *)Malloc(15213);
return; /* 엥 님아 왜 반환해요. */
}
포인터 변수 p를 malloc으로 할당해줬는데 free로 메모리 해제를 안해줬어요..
까먹었습니다... 예.. 프로그래머가 까먹었어요...
가비지컬렉터는 무료로
free를 호출해 줍니다.
가비지 컬렉터는 더 이상 프로그램에서 사용하지 않는 블록들은 자동으로 반환해줘요.
사용하지 않는 블록들을 가비지(쓰레기)라고 해요.
가비지 컬렉터가 이런 쓰이지 않는 블록들을 주기적으로 확인해서 주기적으로 free를 실행해줘요.
가비지 컬렉션을 수행하는 방식도 여러가지가 있긴한데
오늘은 책에 적혀있고 가장 기본적인 방식인 Mark & Sweep 알고리즘 방식에 대해서 작성하겠습니다.
Mark & Sweep 알고리즘은 딱 두 가지 단계로 이루어져 있어요.
그 과정이 어떻게 이루어지는지 살펴볼게요.
먼저, 프로그램이 사용 중인 메모리 블록들을 표시합니다. 이를 Marking 이라고 해요.
이 과정이 끝나면 살아있는 블록에는 표시가 되어있을거고, 쓰이지 않는 블록들은 표시가 없게 될거예요.
왜냐하면
힙 영역에 있는 블록들을 스택,전역변수,레지스터에 있는 애들로만 따라갔잖아요
저기서 갈 수 없는 애들은 연결이 안되서 Marking이 안되어있는거임!!
살아있는 ( 연결이 되는 ) 블록들은 Marking 단계에서 표시가 되어있으니까
표시가 없는 블록들을 free로 해제해주면 되는거예요!!
if (block is marked)
unmark it; // 다음 GC를 위해 표시를 지움
else if (block is allocated)
free(block); // 가비지니까 회수!
oh my god , it's so easy
가비지 컬렉터 짱짱맨이죠? 근데.....
Mark & Sweep 방식은 가비지 컬렉팅하는 C를 위한 적절한 방법인데
왜냐하면 위 방식이 블록을 이동시키지 않은 채로 동작하기 때문이예요.
그런데 여기서 중요한 문제가 하나 있어요.
❗ C 언어는 변수 타입을 런타임에 모릅니다.
어떤 값이
컴파일 타임에는 알 수 있지만 런타임에는 모릅니다.
그래서 가비지 컬렉터는 isPtr(p)라는 함수로 p가 진짜 포인터인지, 어떤 블록을 가리키는지 찾아야 하는데,
이게 C에서는 걍 킹받는 일입니다.
0x0000001111이 정수 15인지 포인터인지 어케알아요 ㅠ
그래서 어떻게 하냐면?
그냥 보수적 판단해요.
조금이라도 포인터일 수 있는 값은 다 포인터라고 생각해서 보수적으로 처리합니다.
진짜로 사용 중인 블록은 놓치면 안 되니까,
안 쓰는 블록을 가끔 못 지우더라도 괜찮다는 식으로 동작해요.
그래서 이런 방식을 Conservative GC (보수적인 가비지 컬렉터) 라고 부릅니다.
Conservative GC는 안정적이긴 하지만, 단점도 있어요.
이게 바로 C 언어에 딱 맞는 절충안입니다.
마무리
제가 프론트엔드로 일 할 때도 , 자바스크립트의 가비지 컬렉션이 단골 CS 면접 질문이었는데요.
이렇게 정글에와서 정확히 알게되니까 기분이가 좋크든요~ > <
말록 쓰고 프리하는 걸 잊지말록....