개발업무를 진행하다보면, 불필요한 작업 또는 데이터로 인해 메모리 리소스가 부족할때가 종종 있다.
이때문에 개발자는 항상 할당중인 메모리를 지속적으로 모니터링하고 관리해야 한다.
오늘 알아볼 Garbage Collection, 이하 GC라는 개념은 프로그래밍에서 자동으로 메모리 관리를 수행하는
프로세스를 의미한다. 프로그램이 실행되는 동안 동적으로 할당된 메모리(힙 메모리)영역에 객체들이 생성되는데,
이 중 *더 이상 필요하지않게 된 메모리(아래에서 구체적으로 설명하겠다)와 같은 garbage, 즉, 쓰레기가 된 메모리를
자동으로 식별하고 회수하는 역할을 한다.
1. 그런데, '어떤 객체'를 '어떤 근거'로 garbage로 판단하게 되는걸까?
먼저, GC의 기본 원리는 '도달 가능성(reachability)'을 근거로 가비지 메모리의 여부를 판단하게 된다.
여기서 도달가능성이란 특정 메모리 객체가 프로그램의 루트집합(root set)으로부터 직접적 또는
간접적으로 접근할 수 있는 경로가 하나라도 있다면 이 메모리는 '도달가능성'이 있는 객체로 간주한다.
이 루트 집합에는 '글로벌 변수', '지역변수', '활성메소드의 매개변수'가 포함되는데, 아래 예시들에 해당하는 요소들을 말한다.
# global variable 예시
global_list = [1, 2, 3] # 프로그램 전체에서 접근 가능한 리스트
def use_global():
print(global_list)
use_global()
# [output]
# [1, 2, 3]
# local variable 예시
def local_example():
local_var = "Hello" # 지역 변수
print(local_var)
local_example()
# [output]
# Hello
# 활성 메소드의 매개변수(매서드내 parameter)
def param_example(param):
print(param)
param_example("This is a parameter")
즉, 위와 같은 루트 집합(root set)의 요소들로 부터 직접적이거나 간접적으로 접근할 수 있는 경로가 있을때 이 객체를
우리는 흔히 '참조'하고 있는 객체라고 표현한다.
참조 중인 객체는 '사용중인 객체(메모리)'라는 의미로 볼 수 있고, 해당 메모리는 GC에서 제거할 대상이 아닌것이다.
그리고, 이 반대에 해당하는, 루트 집합의 요소들중 '참조 되고있지 않은', '접근할 수 있는 경로가 없는' 객체의 경우
GC에서 삭제 대상이 되는 객체로 분류할 수 있는 것이다.
그럼, 간단한 예시를 통해 살펴보자.
1) 초기상태
루트집합 -> A -> B -> C
예를들어, 객체 A, B, C가 있고, A는 B를 참조하고, B는 C를 참조하고 있다고 가정하자.
그리고, 루트집합에는 객체 A만 포함되어 있다.
2) 변경발생(Garbage 발생)
루트집합 -> A
B -> C
그리고, 작업 진행도중 객체 A가 B를 더이상 참조하지 않게 된다.
이제 루트 집합에서 B나 C로 갈 수 있는 경로가 사라졌다.
위 케이스에서 Garbage Collection은 어떻게 동작하게 될까?
GC가 참조되지 않는 객체를 추적하는 과정은 크게 '마킹(Marking)'과 '스위핑(Sweeping)'단계로 구분할 수 있다.
먼저 '마킹'단계에서는,
루트집합에서 시작하여 접근할 수 있는 모든 객체에 대해 '탐색'을 수행한다.
가비지 컬렉터는 이 루트 집합으로 부터 출발하여 객체들을 따라가며(reachable objects), 각 객체에
'마킹'을 한다. 이렇게 해서 도달할 수 잇는 모든 객체가 마킹되며, 도달할 수 없는 객체들은 마킹되지않은
상태로 남게된다.
그 다음, '스위핑'단계가 진행된다.
스위핑 단계에서는 힙 메모리 전체를 스캔하며, 마킹되지 않은 객체들을 찾아 메모리를 회수한다.
이 과정에서 실제로 해당 객체가 사용하고 있던 메모리가 해제되며, 다른 목적으로 재사용될 수 있게 된다.
위 예시에서 마킹과 스위핑이 진행되는 과정을 적용해보면,
1) 가비지 컬렉터가 작동을 시작하면, 루트집합에 있는 A부터 탐색을 진행한다.
A는 도달가능한 객체이므로 마킹된다.
B와 C는 루트집합에서 도달할 수 없으므로 마킹되지않고,
2) 스위핑 단계에서 마킹되지 않은 B와 C객체의 해당 메모리는 회수되게 된다.
2. Python에서는 gc 모듈을 통해 garbage collection을 적용할수 있다.
파이썬의 'gc' 모듈을 사용하면 가비지 컬렉터에 대한 제어와 가비지 컬렉션 프로세스에 대한 정보를
얻을 수 있다. 이를 통해 특정 객체에 대한 참조 체인을 확인하거나, 어떤 객체들이 현재 도달 가능한 상태인지를 파악할 수 있다.
1) 객체 참조 체인 확인하기
import gc
class MyClass:
pass
# 객체 생성
obj = MyClass()
ref = obj # obj에 대한 추가 참조 생성
# 가비지 컬렉션 활성화 및 객체 추적
gc.collect()
gc.set_debug(gc.DEBUG_LEAK)
# obj에 대한 참조자 조회
referrers = gc.get_referrers(obj)
print(f"obj의 참조자: {referrers}")
# obj가 참조하는 객체 조회
referents = gc.get_referents(obj)
print(f"obj가 참조하는 객체: {referents}")
위 gc 모듈을 사용한 코드를 통해 MyClass클래스의 변수인 obj가 참조중인 객체들의 목록과 obj를 참조하고있는 객체들의 목록을 차례로
출력해볼 수 있다.
gc.collect()
마지막으로 gc.collect()라는 클래스를 호출하게 되면, 위에서 설명한 garbage collection 과정을 수행하여,
참조되지않은 객체, 비 참조 메모리를 모두 회수할 수 있게 된다.
Reference
https://medium.com/dmsfordsm/garbage-collection-in-python-777916fd3189
'DEVELOP_NOTE > Python' 카테고리의 다른 글
Python Decorator @ 사용방법 완벽 이해하기! (0) | 2024.02.07 |
---|---|
Python Generator (a.k.a. 'yield') (2) | 2024.02.06 |
[REFACTORING] dictionary에 'key' 존재 유무에 따른 데이터 채우기 (0) | 2024.02.05 |