프로그램이 실행되기 위해서는 먼저 프로그램이 메모리에 로드 되어야 한다.
또한, 프로그램에서 사용되는 변수들을 저장할 메모리도 필요하다.
컴퓨터 운영체제는 프로그램 실행을 위해 메모리 나누어 관리하고 있다.
코드 영역(텍스트 영역)
데이터 영역
Stack 영역
Heap 영역
정적 메모리 할당
동적 메모리 할당
C에서 변수에 값을 할당하면 메모리에 해당 값이 바로 저장되지만,
파이썬에선 object를 만들어서 변수가 그 객체를 가리키는 방식이다.
a = 1
print(type(a))
<class 'int'>
a의 타입은 class이다. 그리고 이것을 구체화한것이 int 객체이다.
그리고 아래와 같은 경우,
b = a
a에 의해서 만들어진 1이라는 int 객체를 b가 그냥 가르키기만 한다.
즉, a와 b가 동일한 int 객체 1을 가리키는 것이다.
a = a+1
이 상황에서는 2(1+1)이라는 새로운 int 객체를 생성한다.
그리고 이제 a는 새로 만드어진 int 객체 2를 가리키게 된다.
만약 새로운 변수를 만들어서 다음과 같은 상황인 경우,
c = 1
1이라는 int 객체는 위에서 이미 생성했으니 다시 만들 필요가 없다.
그냥 c라는 변수가 기존의 int 객체를 가르키기만 하면 된다.
첫 번째절 메모리 구조에 대해 이야기하면서 파이썬의 경우 Heap사용이 조금 다르다고 이야기했었다.
C나 java는 mallo 함수를 사용하여 동적할당(Dynamic allocation)을 할 수 있지만, 파이썬은 동적 할당 기능이 없다. 동적 할당은 정적 할당 대비 필요한 경우에만 메모리를 유동적으로 할당할 수 있어서 메모리를 효율적으로 관리하게 해준다. 따라서 Heap영역의 사용은 메모리를 낭비없이 생산적으로 사용하기 위해서 필수적이다. 그럼 파이썬에서는 어떻게 사용할까?
Python Memory Manager
Heap 안에 메모리 영역을 interpreter가 포인터를 사용해 영역의 범위를 조정함으로써, interpreter가 자동으로 메모리를 OS로 할당하지 않고 가지고 있다가, 사용될 때 메모리르 재소용하는 방식으로 운영된다.
즉, 메모리 할당에 있어 OS의 과부화를 줄여주는 방식이다.
변수와 함수가 호출됨에 따라 그에 맞는 메모리를 그때 그때 Python Memory Manager가 운영체제와 소통하면서 할당하고 결과 값이 return되거나 변수와 함수가 사용을 멈추었을 때, 바로 메모리가 소멸되는 방식이다.
일반적으로 스토리지에 동적할당 이 필요한 경우
그러나 파이썬에서는 메모리 동적할당은 크게 중요하지 않다. 그래서 C에서 사용하는 동적할당 API를 호환하여 사용한다.
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)
위 코드는 f1()과 f2()를 정의한 뒤, main에서 변수할당과 사용자 지정 함수를 동작시키고 있다.
왜냐하면 int 객체 10을 아무도 가르키고있지 않기 때문이다. 이것이 reference counting이 0이 됨에 따라 object가 사라지는 Garbage collecoting이다.
❗️파이썬은 reference counting을 이용해 메모리를 관리한다.
메모리 재할당은 필요한 메모리의 양이 바뀌어서 다시 메모리를 할당 받는 것이다.
메모리 재할당 시 뒷편에 공간이 충분하다면 그냥 공간을 늘려주면 된다.
그러나 메모리 재할당 과정시 뒷편에 메모리에 충분한 공간이 없다면 데이터 할당영역을 옮겨서 재할당해야한다.
빈번한 메모리 재할당은 새로 메모리를 할당받아야하므로 가급적 피하는 것이 좋다.
한번에 깔끔하게 메모리를 받아내는 것이 좋다.
for문 속에서 append를 사용하게 되면 메모리 재할당이 이루어져서 메모리를 효율적으로 사용하지 못한다.
참조