모든 언어에서 변수를 등록하면 변수에 메모리가 메모리의 주소값이 할당된다. 그리고 변수의 크기만큼 메모리를 쓰게되는데 이 메모리는 우리가 알고있는 RAM을 의미한다.
RAM은 휘발성 저장공간이다. 용량이 적지만 컴퓨터를 끄면 사라지는 데이터 공간이다. 대신에 빠르다.
만약 이 메모리 공간을 비효율적으로 사용하여 사용자의 메모리를 많이 잡아먹는다면 흔히 렉이 걸린다고 하듯 프로그램이 느려질 수 있다. 따라서 이 공간을 효율적으로 관리하는것이 프로그램을 빠르게 돌리는데에 있어서 중요하다.
가비지 컬렉션은 쓰지않는 값을 모아서 소멸시킨다.
Cpython에서 가비지 컬렉션은 두가지 규칙을 기준으로 작동한다.
레퍼런스(reference) = 참조
파이썬은 변수의 레퍼런스 카운트가 0이 되었을때 그 변수는 소멸 되상이 되고 일정 규칙에 의해 가비지 컬렉션이 실행될 때 이 값은 소멸된다.
s = "hello world"
위와 같이 변수를 등록하게 되면 s라는 변수는 "hello world" 문자열을 '참조'한다. 참조하는 변수가 1개가 되어 레퍼런스 카운트는 1이 된다.
s2 = s
위와 같이 선언하면 s2 또한 s가 가리키는 문자열을 똑같이 참조하게 된다. 따라서 레퍼런스 카운트는 2가 된다.
s = 123
만약 위와 같이 s가 가리키는 값을 123으로 바꿨다.
그렇다면 "hello world"는 레퍼런스 카운트가 0이 되고, 123은 레퍼런스 카운트가 2가 된다. 어떠한 변수도 "hello world"를 쓰고 있지 않다. 따라서 곧 가비지 컬렉션에 의해 "hello world"는 소멸 된다.
순환참조 일때는 레퍼런스 카운트로는 메모리에서 해제 될 수 없다.
>>> a = []
>>> a.append(a)
>>> del a
a는 자신을 계속해서 가리키게 되는 순환참조 객체이다.
id(a), id(a[0]), id(a[0][0]) 모두 같은 주소값을 가리킨다.
가비지컬렉터는 내부적으로 3세대로 구분되어 관리된다.
제일 최근 생성된 객체 0세대부터 오래된 객체인 2세대 까지 있고, 0세대에서 가비지 컬렉션이 작동하고 살아남으면 1세대, 그리고 1세대에서 살아남으면 2세대로 이동한다.
이렇게 나뉘는 이유는 새로운 객체는 소멸될 확률이 높고 오래된 객체는 소멸될 확률이 낮기 때문에 세대를 나누어 가비지 컬렉션이 실행될 때 확인할 객체를 줄이기 위해서 이다.
>>> import gc
>>> gc.get_threshold()
(700, 10, 10)
gc모듈을 사용하면 가비지 컬렉터의 임계값을 확인할 수 있다.
0세대의 객체 700개가 모이면 0세대에서 가비지 컬렉션이 실행된다. 여기서 살아남은 객체들이 1세대로 이동 후 카운터를 1 증가시킨다.(객체의 갯수만큼이 아님) 이런식으로 증가시켜서 객체가 7000번 생성될 때 1세대의 가비지 컬렉션이 일어난다. 2세대는 7만번만에 한번 일어난다.
>>> import gc
>>> gc.get_count()
(107, 9, 3)
>>> gc.collect()
146
>>> gc.get_count()
(21, 0, 0)
gc.get_count()
는 각 세대의 객체 수를 확인할 수 있다.
gc.collect()
는 가비지 컬렉터를 실행시킨다.
No.. 가비지 컬렉션을 수행하려면 응용 프로그램을 완전히 중지해야 한다. 따라서 자주 작동 시키는것도 좋지 않다. 그렇다고 짧게 작동시키면 메모리 공간에 가비지가 많이 쌓이게 된다.
대규모 서비스라면 고민해 볼만 하지만 대부분의 개발자에게는 가비지 컬렉션과 관련된 Python의 표준 동작이 충분하다. 가비지 컬렉션 동작을 수정하는것은 Python의 장점인 생산성을 낮추는 행위가 된다. 메모리 부족이라면 메모리를 늘리자!
아래 블로그를 많이 참고하였습니다.
Garbage Collection in Python