[OS] Garbage collector, 그리고 파이썬의 Memory 사용

EMMA·2022년 5월 7일
0
post-thumbnail

Garbage collection

메모리 관리 기법 중에 하나로, 말 그대로 '쓰레기 수집'이다.
garbage collection을 이해하려면 아래 2가지 용어를 먼저 이해할 필요가 있겠다.

  • 메모리 정적 할당
    • 메모리의 크기 자체를 하드 코딩해, 프로그램 실행 단계에서 이미 사용 크기가 정해져 있음
    • 프로그램이 끝나면 OS에서 알아서 회수함
  • 메모리 동적 할당
    • 프로그램을 실행하는 동안 사용할 메모리 공간을 할당하는 것
    • 작성자가 명시적으로 해제하거나 쓰레기 수집이 발생해야 프로그램 종료 후 회수됨

따라서 garbage collection은 동적 할당된 메모리 영역 중에서,
어떤 변수도 가리키지 않는(사용하지 않는) 영역을 탐지해 제거하는 작업을 말한다.

출처: 위키백과 - 쓰레기 수집 (사실 너무 이뻐서 갖고옴)

메모리 누수(memory leak)가 계속될 경우, 메모리가 고갈되어 프로그램이 중단되기 때문에 해당 역할은 중요하다.

Collecting methods

대표적으로 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 기준으로 분석하면,

  • a라는 변수 생성 시 +1
  • sys.getrefcount에 a 전달 시 +1

파이썬의 메모리 관리

파이썬은 동적 메모리 할당을 기본으로 한다.
즉, 프로그래머가 직접 관리하는게 아니라 reference count와 garbage collection으로 메모리 관리가 된다는 뜻.

  • 객체가 생성되면, 파이썬은 객체를 메모리와 세대 0에 assign한다
  • 임계치를 오버하는 경우, collect_generations()를 실행한다. 2세대부터 역순으로 검사한다.
  • 객체 수가 임계치를 넘으면 gc.collect()를 실행해 garbage collection process를 수행한다
  • gc.collect()는 unreachable 객체의 개수를 반환한다. 도달할 수 없는 객체란, 더이상 사용되지 않는 객체를 말한다. 이들은 메모리에서 해제된다.
  • reachable 객체는 다음 세대로 이동된다.

추가) 파이썬의 메모리 할당

메모리에는 크게 4개 구조가 있다.

  • 코드 영역: 프로그램 실행할 코드를 저장
  • 데이터 영역: 전역변수 등을 저장
  • 힙 영역(heap): 동적 할당된 변수나 메소드 저장
  • 스택 영역 (stack): 지역변수 등을 저장

예를 들어, 아래와 같은 코드가 있다고 가정해보자.

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
  • main에서 y=5를 선언했다. 5는 heap영역에, 변수 y는 stack 영역에 저장된다.
  • 이어서 선언한 zf1() 는 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

profile
예비 개발자의 기술 블로그 | explore, explore and explore

0개의 댓글