메모리 관리 기법 중에 하나로, 말 그대로 '쓰레기 수집'이다.
garbage collection을 이해하려면 아래 2가지 용어를 먼저 이해할 필요가 있겠다.
따라서 garbage collection은 동적 할당된 메모리 영역 중에서,
어떤 변수도 가리키지 않는(사용하지 않는) 영역을 탐지해 제거하는 작업을 말한다.
메모리 누수(memory leak)가 계속될 경우, 메모리가 고갈되어 프로그램이 중단되기 때문에 해당 역할은 중요하다.
대표적으로 3가지가 있다.
Mark and sweep (표시하고 쓸기)
각 메모리 영역에 1비트씩 남겨둔 후, 변수가 가리키는 영역과 그 영역이 가리키는 또 다른 영역을 모두 '사용 중'으로 표시한다. 이 때 '사용 중'으로 표시되지 않은 나머지 영역을 없애는 기법이다.
Generational Garbage Collection (세대 단위 쓰레기 수집)
할당된 시간에 따라, 0세대-2세대까지 세대 기준으로 구분한 뒤 각각 다른 메모리 영역에 할당한다.
한 세대의 메모리 영역이 꽉 차면(threshold, 임계치), 살아남은 객체는 더 오래된 세대의 메모리 영역으로 옮긴다.
garbage collector 입장에서는 새롭게 할당된 영역 위주로 체크하면 되며, JAVA 등이 해당 기법을 사용하고 있다.
파이썬도 이 세대 단위 쓰레기 수집을 사용한다.
각 세대 별 threshold의 임계치가 초가되면, gc가 실행된다.
import gc
#threshold 0, 1, 2 의 임계값
print(gc.get_threshold())
>>> (700, 10, 10)
#각 세대 별 객체 수
print(gc.get_count())
>>> (455, 9, 0)
#임계치 설정(변경)
gc.set_threshold(1000,15,15)
print(gc.get_threshold())
>>> (1000, 15, 15)
Reference count(참조 횟수 계산)
기본적으로 reference count는 객체가 참조될 때마다 +1, 해제될 때 -1로 계산된다. 이렇게 각 객체의 참조 횟수를 count하여, 0이 되면 해당 객체를 해제하는 방식을 말한다.
순환참조가 걸리거나, 횟수가 0인 객체가 가리키는 다른 객체들의 횟수를 0으로 계산하는데 일정한 시간 소요가 필요하는 등의 단점이 있긴 하다.
하지만 그럼에도 불구 해제가 빠르게 이뤄지고 시점을 대략 예측할 수 있어 (카운트가 0이 되는 순간 해제, 해제하는 시점에는 캐시에 있을 가능성이 높아짐) 많이 사용된다.
파이썬은 참조 횟수 방식도 사용한다.
import sys
#예시 1
a = 'Hello'
sys.getrefcount(a)
>>> 2
#예시2
class A:
pass
a = A()
b = a
print(sys.getrefcount(a))
>>> 3
예시1 기준으로 분석하면,
sys.getrefcount
에 a 전달 시 +1 파이썬은 동적 메모리 할당을 기본으로 한다.
즉, 프로그래머가 직접 관리하는게 아니라 reference count와 garbage collection으로 메모리 관리가 된다는 뜻.
collect_generations()
를 실행한다. 2세대부터 역순으로 검사한다. gc.collect()
를 실행해 garbage collection process를 수행한다 gc.collect()
는 unreachable 객체의 개수를 반환한다. 도달할 수 없는 객체란, 더이상 사용되지 않는 객체를 말한다. 이들은 메모리에서 해제된다. 메모리에는 크게 4개 구조가 있다.
예를 들어, 아래와 같은 코드가 있다고 가정해보자.
def f2(x):
x = x+1
return x
def f1(x):
x = x*2
y = f2(x)
return y
#main
y = 5
z = f1(y)
print(z)
>>> 11
y=5
를 선언했다. 5는 heap영역에, 변수 y
는 stack 영역에 저장된다. z
와 f1()
는 stack영역에 추가되고, f1
에 담은 y
는 5를 참조한다. 5는 다시 f1(x)
의 x
에 전달된다. f1(5)
가 실행되면, x=x*2
에 의해 10은 heap영역에 저장되고 x
는 10을 가리킨다. y=f2(x)
가 실행되면, f2()
는 stack영역에 저장되고 10을 가리킨다. x=x+1
로 인해 11이 heap영역에 추가되고, x
는 11을 가리키게 된다. return x
로 인해 11이 f1(x)
의 y
에 담기고, return y
로 인해 z
에 11이 담긴다. f1()
과 f2()
는 stack영역에서 사라진다. 참고 자료
https://ko.wikipedia.org/wiki/쓰레기_수집_(컴퓨터_과학)
https://www.youtube.com/watch?v=24f2-eJAeII
https://blog.winterjung.dev/2018/02/18/python-gc
https://youtu.be/24f2-eJAeII
https://hkim-data.tistory.com/182