OS는 프로그램을 프로세스로 실행하게 되고, 해당 프로세스는 메모리에 코드,데이터,힙,스택 영역을 할당받게 된다.
이 때, 힙, 스택 영역에 할당된 메모리들을 해제하는 동작을 Garbage Collector가 수행하게 된다.
Garbage Collector의 동작은 각 언어별로 언제/어떻게 동작하는지 차이점이 있으며, 특히 동적 객체가 할당되는 힙 영역에서의 동작은 반드시 숙지하고 있어야 한다.
Python의 Garbage Collecting은 다음 2 가지 방식으로 동작한다.
레퍼런스 카운팅 방식의 가비지 컬렉팅은, 어떤 객체가 참조되고있는 횟수를 카운팅하고 0이 될 경우 메모리에서 해제하는 방식이다.
python standard library의 sys모듈로 특정 객체의 reference counts를 확인할 수 있다.
>>> import sys
>>> a = 'hello'
>>> sys.getrefcount(a)
2
위 코드에서, 'hello'는 힙에 할당되고 a는 지역변수라는 가정 하에 스택에 할당되어 'hello'가 위치한 힙의 주소를 갖고 있다.
이 상황에서 a의 레퍼런스 카운트는 2이다.
첫번째는 a가 위치한 스택의 주소가 콜스택에서 참조되고 있기 때문이며, 두번째는 getrefcount()에 전달될 때이다.
만약 콜스택에서 a가 위치한 레벨의 함수가 실행 완료되면, 콜스택에서 참조하고 있는 a가 사라지기에 reference count는 0이되고 gc에 의해 메모리에서 해제된다.
>>> l = []
>>> l.append(l)
>>> del l
위 코드의 l과 같이, 만약 어떤 동적 객체가 서로를 참조(순환참조)하고 있다면 Reference counting 방식에서는 해당 객체들은 메모리에서 해제될 수 없다.
이러한 경우를 방지하기 위해, 파이썬에서는 Generational garbage collection이 순환 참조를 탐지하고 메모리에서 해제해준다.
순환 참조는 컨테이너 객체(e.g. tuple, list, set, dict, class)에 의해서만 발생한다. 컨테이너 객체들만이 다른 객체의 참조를 보유할 수 있기 때문이다.
순환 참조 해결을 위해 아래의 순서로 동작한다.
Generational garbage collection을 이해하기 위해서는 아래 세대와 Threshold 개념에 대해 인지하고 있어야 한다.
Python GC는 객체를 0~2세대로 분리하여 관리하고, 세대가 낮을 수록 더욱 자주 garbage collecting을 수행한다.
이 동작은 어린 객체가 오래된 객체보다 해제될 가능성이 높다는 가설에서 근거한다.
세대별 객체의 수가 정해진 Threshold를 초과하면, 임계치가 초과된 세대의 객체에 대해 수행하게 된다.
세대별 Threshold는 gc모듈의 get_threshold()를 통해 확인할 수 있다.
>>> import gc
>>> gc.get_threshold()
(700, 10, 10)
>>> gc.get_count()
(121, 9, 2)
위 코드를 기준으로,
(700, 10, 10)에서 threshold 0(700)의 의미는 0세대 객체가 700개를 초과하면 가비지 컬렉팅이 수행된다는 의미이다. gc모듈은 0세대 객체가 threshold 0을 초과하면 가비지컬렉팅을 수행하고, 남아있는 객체들을 1세대 객체로 옮기며, 1세대의 count를 1 증가 시킨다.
threshold 1(10)의 의미는 약간 다르다.
threshold 0은 0세대 객체 수에 대한 임계값이었지만, threshold 1은 0세대 가비지컬렉팅이 발생한 횟수에 대한 임계치이다.
threshold 2(10) 역시, 1세대 가비지컬렉팅이 발생한 횟수에 대한 임계값이다.
즉, 0세대 가비지 컬렉팅이 객체생성 700번만에 발생한다면,
1세대는 7000번 만에, 2세대는 70000번 만에 가비지 컬렉션이 수행된다는 의미이다.
레퍼런스 카운팅 방식은 python에서 자동으로 수행되며,
파이썬의 가비지 컬렉터는 세대별 가비지 컬렉션만을 수행한다. 세대별 가비지 컬렉션은 코드에서 아래와 같이 사용할 수 있다.
참조URL:
https://dgkim5360.tistory.com/entry/understanding-the-global-interpreter-lock-of-cpython
인스타그램의 GC 비활성화 https://luavis.me/python/dismissing-python-garbage-collection-at-instagram
https://blog.winterjung.dev/2018/02/18/python-gc