Python/ETC / / 2023. 9. 25. 13:33

[파이썬 기본 다지기] Python에서의 메모리 관리 1편

*이 글에서는 표준 구현체인 CPython을 기준으로 진행합니다

In Python, Everything is an Object

파이썬에서는 모든 것이 객체다. 이 말을 들어본 적이 많을 것이다. 파이썬에서는 클래스(class), 함수(function), 그리고 정수(integer), 실수(float) 등등 모든 것이 객체이다. 각각의 파이썬 객체는 값(Value), Type(타입), Reference Count(참조 카운트)를 포함하고 있다.

 

그래서 만약에 다음과 같은 코드를 작성했다고 쳐보자.

a = 10

만약 위의 코드가 실행되면, CPython은 타입이 integer인 객체를 만들고 이 객체를 메모리에 할당한다. 이때 value에는 10dl Reference Count에는 1이 들어가는데, 이는 나중에 설명하겠지만, a = 10에서 변수 a에 10이 할당되었기 때문에 10을 참조하는 카운트가 1이 늘은 것이라고 보면 된다. 

 

그래서, 모든 것이 객체다

귀도 반 로섬(Guido van Rossum)은 The Python Lanugage Reference(2012)에서 다음과 같이 - "파이썬에서의 객체는 데이터의 추상화이다"라고 말한 적이 있다. 이를 생각해보면, 파이썬에서 모든 데이터는 객체나 객체들 관의 관계로서 표현될 수 있다는 것이다. 이는 불리안(boolean), 정수(integer), 실수(float), 문자열(string), 그 외 등등 심지어 함수(function)까지도, 모든 것이 객체로서 실행된다.  

 

불변 데이터와 그렇지 않은 데이터

Python에서는 어떤 데이터는 변하고 어떤 데이터는 변하지 않는다. 불변 데이터에는 숫자(integer, float), 문자열(string), 튜플(tuple)이다. 그 반대에는 리스트(list), 사전(dictionary), 그리고 셋(집합 혹은 set)이 있다. 간단하게 불변데이터에 대해 비유를 하자면, 마치 볼 수만 있는 혹은 참조만 할 수 있는 데이터라 보면 된다. 수정은 불가능한 데이터이다. 이를 파이썬의 객체 개념까지 추가하면, 객체를 참조 및 불러오기, 할당만 가능하며 이 데이터 객체 자체는 수정이 불가능하다. value는 수정 불가능하다. 여기에 수정이 가능한 개념이 추가되면 변화가 가능한 데이터가 된다.

 

그렇다면 이 불변 데이터에 대한 취급이 중요한 이유는 무엇일까?

 

일단 불변 데이터 - 불변 객체 - 의 경우는 변화 가능한 데이터보다 접근이 쉽다. 하지만 이 불변 데이터의 표면상의 수정은 Python에서 결국 또 다른 하나의 객체를 생성하기에 새로운 메모리 공간에 무조건 할당해야 한다. 하지만 그렇지 않은 데이터는 쉽게 수정이 가능하며 또다른 복사본을 만들 필요가 없다. 그렇다면 이러한 메모리 차지를 하는 불변 데이터는 좋지 않은 것일까? 다르게 생각하면 디버깅에 좀 더 유리할 수 있는 면이 있다. 값이 바뀌지 않기에 객체의 상태를 쉽게 추적할 수 있게 해준다.

 

이는 함수에 인자로 넘길 때도 차이가 나타난다. 불변 데이터의 경우는 Call by Value로서 넘어가게 된다. 하지만 list와 같은 데이터는 Call by Reference로 넘어가게 된다. 이는 실제 데이터의 id값을 확인해 보면 알 수 있다.

 

a = 10
b = [0,0,0,0]

def immutable(a):
	i = a + 10
    print(id(i))
    
def mutable(b):
	b[0] = 10
    i = b
    print(id(i))
    
    
if __name__ == "__main__":
	print(id(a))
    print(immutable(a))
    print(id(b))
    print(mutable(b))

위와 같은 코드를 짰다고 가정해보자. 편의상 밑에는 코드를 간단하게 Python IDLE에서 실행해 본 것이다.

불변 데이터를 보낸 함수에서는 값이 10이 증가하고 이는 새로운 객체 생성으로 CPython에서는 이루어지기 때문에 둘의 id값이 다른 것을 알 수 있다. 하지만 단순 수정만 한 mutable함수의 경우 Call by Reference로 보내졌기에, 즉 주소값이 보내졌기에, 같은 주소값을 가지고 있는 것을 확인할 수 있다.

CPython에서의 메모리 할당

파이썬에는 객체와 데이터 구조를 저장하는 Private Heap이 존재한다. 파이썬 메모리 매니저는 파이썬에서 프로그램이 실행될 때 다양한 프로그램들의 메모리 할당과 비할당의 책임을 가지고 있다.

Heap Space v.s. Stack Space

x = 10
y = korea

파이썬에서 Heap Space에는 파이썬의 객체와 데이터 구조를 저장한다. 반면에, Stack Space에는 Heap Space에 있는 객체에 대한 참조체를 가지고 있다. 만약 앞에서 a = 10인 코드를 작성하고 b = a인 코드를 작성했다면 어떻게 될까? 다음과 같은 코드를 한 번 살펴보자.

a = 10
b = a

이 때 Reference Count가 2가 된다. 이유는 간단하다. b = a를 했고, 이에 b에도 a의 값인 10이 할당되었다. 여기서, b의 경우 10을 바라보기에 a와 b 둘 다 10을 바라보게 되고 이는 곧 두개의 참조자가 10이라는 객체에 생긴 것이다. 따라서 Reference Count는 2가 된다. 만약 이 다음에 a += 10을 하게 된다면 어떻게 될까?

 

a = 10
b = a
a += 10

여기서 생각해야할 것은 "Everything is an object" 이다. a += 10을 하면 새로운 값인 20이 생성되고 이 값 마저도 새로운 객체이다. 즉, CPython에서 위에서 언급했다시피 새로운 20이라는 객체를 만든다. 다시 생각해야할 부분이 생긴다. 바로 a와 b가 가르키는 곳이 어디인가 이다. a는 이제 새로운 값인 20을 얻었으므로 20을 참조한다. 그렇다면 b는? 기존에 10을 계속 참조한다. 하지만 여기서 차이가 난다. 바로 a와 b가 동시에 참조하던 20, 즉 두개의 참조 카운트가 이제는 하나로 줄어드는 것이다.

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유