[Python] 가비지 컬렉션, 수정 가능한 객체와 불가능한 객체

2 분 소요

#1. 가비지 컬렉션

>>> s = 'Garbage Collection'
>>> s
'Garbage Collection'

보통 ‘변수에 문자열을 저장했다.’, ‘변수에 객체를 저장했다.’라는 식의 표현을 하지만 파이썬의 ‘가비지 컬렉션’ 메커니즘을 이해하기 위해서는 변수를 ‘포스트잇’으로 이해해야 한다.

“변수 s가 문자열 ‘Garbage Collection’을 참조한다.” “변수 s가 문자열 ‘Garbage Collection’을 레퍼런스한다.”

>>> r = [1,2,3]
>>> r = 'simple'

리스트 [1,2,3]이 소멸되는 시점은 변수 r이 문자열로 참조대상을 바꿨을 때이다. 리스트는 아무도 참조하지 않기 때문에 더 이상 접근할 수 없기 때문이다.

단, 소멸 대상이 되었다고 바로 소멸되는 것이 아니라 우선 소멸 대상으로 등록해 두고 시스템에 시간적인 여유가 생길 때 소멸시킨다.

이러한 일련의 소멸 규칙 및 과정을 가리켜 ‘가비지 컬렉션(Garbage Collection)’이라고 한다.

>>> r1 = [1,2,3]  # 리스트 [1,2,3]의 레퍼런스 카운트는 1
>>> r2 = r1       # 리스트의 레퍼런스 카운트는 2로 증가
>>> r1 = 'simple' # 리스트의 레퍼런스 카운트는 다시 1로 감소
>>> r2 = 'happy'  # 리스트의 레퍼런스 카운트 0. 가비지 컬렉션 대상

객체를 참조하는 변수의 수를 가리켜 ‘레퍼런스 카운트’라 한다. 레퍼런스 카운트가 0이 되면 소멸대상으로 등록되어 조만간 소멸된다.

인터프리터

파이썬 인터프리터는 PVM의 구현 방식에 따라 나뉜다.

  • CPython : PVM이 C언어로 구현. 가장 일반적이고 널리 사용됨.
  • JPython : PVM이 자바로 구현되어 자바에 대한 접근성이 좋다.
  • IronPython : 마이크로소프트사의 닷넷 기반으로 만들어지고 그 위에서 동작하는 인터프리터.

실제 파이썬 프로그램의 실행 주체는 파이썬 가상 머신(Python Virtual Machine)(PVM)이다. PNM에 의해 가비지 컬렉션도 진행이 된다.

파이썬 인터프리터 = 파이썬 코드 변환기 + 가상 머신 + 기본적으로 포함하는 각종 라이브러리들

2. 수정 가능한 객체와 수정 불가능한 객체

immutable

객체가 지닌 값의 수정이 불가능한 객체 ex) 튜플, 문자열

>>> t = (1,2)
>>> id(t)
58040192
>>> t += (3,4)
>>> t
(1,2,3,4)
>>> id(t)
53173968

튜플은 값을 수정할 수 없기 때문에 튜플에 저장된 값을 수정하면 새로운 튜플이 만들어진다.

mutable

객체가 지닌 값의 수정이 가능한 객체 ex) 리스트, 딕셔너리

>>> r = [1,2]
>>> id(r)
51637384
>>> r += [3,4]
>>> r
[1,2,3,4]
>>> id(r)
51637384

리스트는 값을 수정할 수 있기 때문에 리스트를 수정 후에도 리스트의 주소는 바꾸지 않는다.

성격에 따라 달라지는 함수의 정의

>>> def add_last(m,n):
        m += n

>>> r = [1,2]
>>> add_last(r, [3,4])
>>> r
[1,2,3,4]
>>> t = (1,3)
>>> add_last(t,(5,7))
>>> t
(1,3)

위 결과를 보면 mutable 객체인 리스트 r은 add_last함수가 의도한대로 잘 동작한다. 하지만 immutable객체인 튜플 t에 대해서는 의도한대로 튜플에 값이 추가되지 않는다.

튜플의 경우 함수 내의 변수 m에는 새로 만들어진 덧셈 결과가 반영되 튜플이 저장된다. 이때부터 t가 참조하는 튜플과 m이 참조하는 튜플은 별개의 것이 된다.

>>> def min_max(d):
        d.sort()
        print(d[0], d[-1], sep=', ')
>>>     
>>> def min_max2(d):
        d = list(d)
        d.sort()
        print(d[0], d[-1], sep=', ')
>>>
>>> l = [3,1,5,4]
>>> min_max(l)
1, 5
>>> l
[1,3,4,5]
>>> l = [3,1,5,4]
>>> min_max2(l)
1, 5
>>> l 
[3,1,5,4]

리스트는 수정가능한 객체이므로 위 코드와 같이 원본의 저장 순서가 변경되었다. 이러한 상황을 원치 않으면 함수내에서 리스트를 복사한 다음 정렬을 진행하면 된다.