[해키피디아] Garbage Collection

가비지 컬렉션은 간단히 말하자면 동적으로 할당한 메모리 영역을 자동으로 관리할 수 있는 메모리 관리 기법의 하나입니다. 리스프(Lisp)라는 프로그래밍 언어의 메모리 관리를 위해 1959년에 개발되었습니다.

어떻게 관리할까요?

가비지 컬렉션은 동적으로 할당된 메모리 영역 중 더 사용하지 않거나 사용할 수 없는 영역의 메모리를 탐지하여서 자동으로 할당된 메모리 영역을 해제해 줍니다.

가비지 컬렉션을 사용하게 되면 프로그래머가 동적으로 사용하는 메모리 영역을 완벽하게 관리하지 않아도 됩니다! 그래서 memory leak, double free 와 같은 여러 버그를 방지할 수 있습니다.

물론, 가비지 컬렉션에서 사용하지 않는 메모리 영역에 대한 추적에 추가적인 시간과 메모리가 사용되고, 그로 인해 프로그램 성능이 저하 됩니다. 또한, 가비지 컬렉션에 소모되는 시간이 예측하기 어렵다는 단점도 존재합니다.

어떤 것들이 쓰레기 취급 받는건가요?

가비지 컬렉션에서 가비지라고 부르는 것들은 위에서 언급했다시피 더 사용하지 않는 메모리를 말합니다. 예시를 통해 살펴보도록 하겠습니다.

int* p = 0;
 
p = (int*)malloc(1 * sizeof(int));//할당 영역1
*p = 4;
p = (int*)malloc(1 * sizeof(int));//할당 영역2
*p = 5;

위의 코드에서 처음 할당 영역1 주소를 포인터 p의 값으로 주고 p에 그대로 할당 영역2의 시작주소를 줍니다. 이때 할당 영역1의 주소는 다시 찾아서 이용할 수 있을까요?

안타깝게도 위처럼 할당 영역의 시작주소를 따로 저장해놓지 않는 한 가리키는 포인터가 없어진 할당 영역은 다시 사용하기 어렵습니다. 이것들을 가비지 컬렉션에서 가비지로 취급되는 메모리 영역입니다.

Tracing Garbage Collection

첫 번째 작동방식은 Tracing Garbage Collection입니다. 가장 많이 사용되는 가비지 콜렉션 작동방법이며Tracing이 붙은 것 처럼 추적을 기반으로 작동합니다. 해당 작동 방식은 특정한 타이밍에 프로그램 작동 중 할당된 메모리를 조사하여 현재 접근이 가능한지, 불가능한지 알고리즘을 통해 추적하여 쓰레기로 판단되면 가비지 컬렉션을 통해 해당 메모리를 할당 해제하게 됩니다.

Tracing Garbage Collection 방식도 세부적인 작동에 따라 mark and sweep 기법, 삼색 표기 기법, 객체 이동 기법, 세대 단위 가비지 컬렉션 기법으로 나뉩니다.

Reference Counting

두 번째 작동방식은 Reference Counting입니다. 횟수 참조라는 영어의 해석 그대로 각 객체에 대한 참조 횟수를 카운트하여 접근 가능과 불가능을 판단하는 방식이에요. 한 객체가 다른 메모리에서 참조될 때 1을 더하고 참조하는 것을 멈추면 1을 빼서 참조 카운트가 0이 되면 아무도 접근하지 못하는 메모리로 판단해 해당 메모리를 해제하는 방식입니다.

작동방식이 이렇게 간단하다 보니 구현도 매우 간단해 많이 사용됩니다. 하지만 해당 작동방식은 순환 참조 문제가 존재합니다! 순환 참조란 돌고 돌아 자기 자신을 참조하게 되는 경우를 말합니다. 만약 A가 B를 가리키고 B가 A를 가리키게 된다면 두 메모리 모두 접근할 수는 없지만, 참조 횟수가 0이 되지 않아 해제할 수가 없게 됩니다. 카운팅을 하지 않는 메모리를 사용하면 해결될 것 같지만 그렇게 되면 해제된 메모리를 참조할 수 있으므로 카운팅을 하지 않는 메모리를 사용할 수가 없습니다.