[Python]GC

포동동·2023년 6월 20일
2

[Pythonic]

목록 보기
2/4

객체지향 파이썬

파이썬은 객체가 중심이 되며, 참조 변수를 통해 객체에 접근할 수 있다. 예를 들어,

a = 100

이라고 선언이 되면, 100이라는 객체를 a가 참조하는 것이다. 이 때, a는 참조 변수라고 불린다.

만약

a = 100
a = 200

이라고 선언을 하면 200이라는 객체가 추가적으로 더 생기고, a는 이번엔 100이 아닌 200을 참조하게되는 것이다. 이 때, a는 200으로 재할당된다. 이 영상이 간단하고 설명도 잘 되어있다.


가비지 컬렉터(Garbage collector)

메모리 상에 사용하지 않는 객체(garbage)라고 하는데, CPython에서는 reference counting 알고리즘을 통해 이러한 garbage들을 수집한다. 기본적으로는 CPython에서는 GC(Garbage Collector)가 메모리 위를 돌아다니면서 객체마다 reference count를 확인하고 삭제할지 말지 판단하는 방법이다. 만약 reference count가 0이 되면 GC가 가차없이 메모리를 회수한다.

정리하자면, 순서가 아래와 같이 된다.

GC가 메모리를 돌면서 객체들을 스캔한다 -> 객체마다 참조 횟수를 확인한다 -> 삭제 or 잔존

예를 들어, 아래처럼 객체를 만들고 reference count를 출력해보면 아래와 같다.

import sys

x = object() # 객체 생성
sys.getrefcount(x) # reference count 출력
>>> 2

처음에 x라는 참조 변수를 만들어 할당할 때 1번 + sys.getrefcount() 함수를 호출할 때 1번 = 도합 2라는 출력값을 얻을 수 있다.

이 참조변수를 다른 변수에서 또 참조 시키면 그 횟수를 계속 늘어난다.

y = x # y라는 참조변수에 참조시킴
sys.getrefcount(x)
>>> 3

Generation

보통 GC는 빠르게 움직이지만 많은 시간을 잡아먹는 부분이 바로 GC가 메모리를 돌면서 객체들을 스캔한다 스텝이다. 왜냐하면, 어떤 객체는 스캔하고 안 할지 모르기 때문에 우선 전부 다 돌아야하기 때문이다. 이러한 속도 저하를 해결하기 위해서, Python에서는 Generation이라는 개념을 도입했다.

GC에서 얘기하는 Generation이란, 객체들을 Generation(세대)로 나눠 어린 세대(Generation0)의 경우 더 자주 스캔하고, 오래된 세대(Generation2)의 경우 아주 가끔씩만 스캔하는 것이다. 근거는 generational hypothesis라고 한다.

그렇다면, 그 세대를 나누는 기준은 무엇일까? 바로 threshold(임계값)을 통해 조정할 수 있다. 임계값은 gc 모듈을 import 하여 쉽게 확인할 수 있다.

gc.get_threshold()
>>> (700, 10, 10)

아마 출력값은 기본셋팅인 (700, 10, 10) 일 것이다. 각각 무슨 뜻이냐면,
700 = generation0에 할당된 객체수 - generation0에 할당해제된 객체수가 700을 넘으면 DC가 호출된다.
10 = 앞 세대 GC가 몇 번 호출되면 해당 세대 GC가 호출될 것인가. 예를 들어, 가운데 10은 generation0의 GC가 10번 호출되면 그 때 generation1에 해당하는 객체들을 스캔하는 GC가 호출된다는 뜻이다.

예를 들어, 아래와 같이 threshold를 조정할 수도 있다.

gc.get_threshold() 현재 threshold 확인
>>> (700, 10, 10)
gc.get_count() # 현재 각 generation당 객체 수 확인
>>> (376, 11, 10)
gc.set_threshold(20, 1, 1) # threshold 조정
gc.get_count()
>>> (3, 1, 0)

이런식으로 threshold를 조정하면서 메모리를 관리할 수 있다.


주의사항

기본적으로 GC가 존재한다는 건, 컴퓨터가 자동으로 메모리 관리를 하게 해서 개발자의 편의성을 높히기 위해서다. 따라서 이걸 수동으로 설정하고 모니터링 하는 것에 너무 많은 리소스를 쓰지 않도록 해야한다. 또한, GC의 오버헤드를 방지하기 위해 threshold를 너무 높게 설정해서 GC의 실행 빈도를 낮추는 등의 방식은 충분히 다른 방법을 고려해본 뒤 도입해야 한다.

참고1
참고2

profile
완료주의

0개의 댓글