파이썬 메모리 할당

YunGyu Choi·2023년 1월 23일
0

Python

목록 보기
2/2
post-thumbnail

메모리 구조

프로그램이 실행되기 위해서는 먼저 프로그램이 메모리에 로드 되어야 한다.
또한, 프로그램에서 사용되는 변수들을 저장할 메모리도 필요하다.
컴퓨터 운영체제는 프로그램 실행을 위해 메모리 나누어 관리하고 있다.

www.tcpschool.com/c/c_memory_structure

코드 영역(텍스트 영역)

  • 코드 영역은 실행할 프로그램의 코드가 저장되는 영역이다.
  • CPU는 코드영역에 저장된 명령어를 하나씩 가져와서 처리한다.

데이터 영역

  • 데이터 영역은 프로그램의 전역변수, 지역변수들이 저장되는 영역이다.
  • 이 영역은 프로그램 시작과 함께 할당되고, 종료되면 소멸한다.

Stack 영역

  • Stack 영역은 함수의 호출과 관계되는 지역변수와 매개변수가 저장되는 영역이다.
  • 이 영역은 함수의 호출과 함께 할당되고, 끝나면 소멸한다.
  • Stack 영역은 푸시로 데이터를 저장하고, 팝으로 데이터를 인출한다.
  • LIFO방식에 따라 동작하므로 가장 늦게 저장된 데이터가 가장 먼저 인출된다.
  • Stack 영역은 메모리의 높은 주소에서 낮은 주소 방향으로 할당된다.

Heap 영역

  • Heap 영역은 사용자가 직접 관리할 수 있는 메모리 영역이다.
  • 사용자에 의해 메모리 공간이 동적으로 할당되고 해제된다.
  • Heap 영역은 메모리가 낮은 주소에서 높은 주소 방향으로 할당된다.
  • 그러나 파이썬에는 동적할당 기능이 없다. 대신 Python Memory Manager라는 Python/C API를 통해 스토리지를 동적으로 관리한다.


메모리 할당 방식

정적 메모리 할당

  • 프로그램 컴파일시 메모리가 할당된다.
  • 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사용

첫 번째절 메모리 구조에 대해 이야기하면서 파이썬의 경우 Heap사용이 조금 다르다고 이야기했었다.

C나 java는 mallo 함수를 사용하여 동적할당(Dynamic allocation)을 할 수 있지만, 파이썬은 동적 할당 기능이 없다. 동적 할당은 정적 할당 대비 필요한 경우에만 메모리를 유동적으로 할당할 수 있어서 메모리를 효율적으로 관리하게 해준다. 따라서 Heap영역의 사용은 메모리를 낭비없이 생산적으로 사용하기 위해서 필수적이다. 그럼 파이썬에서는 어떻게 사용할까?

Python Memory Manager
Heap 안에 메모리 영역을 interpreter가 포인터를 사용해 영역의 범위를 조정함으로써, interpreter가 자동으로 메모리를 OS로 할당하지 않고 가지고 있다가, 사용될 때 메모리르 재소용하는 방식으로 운영된다.
즉, 메모리 할당에 있어 OS의 과부화를 줄여주는 방식이다.
변수와 함수가 호출됨에 따라 그에 맞는 메모리를 그때 그때 Python Memory Manager가 운영체제와 소통하면서 할당하고 결과 값이 return되거나 변수와 함수가 사용을 멈추었을 때, 바로 메모리가 소멸되는 방식이다.


일반적으로 스토리지에 동적할당 이 필요한 경우

  • 메모리의 공유(Share)
  • 메뫼의 세분화(Segmentaion)
  • 메모리의 사전 할당(Preallocation)
  • 캐싱(Caching)

그러나 파이썬에서는 메모리 동적할당은 크게 중요하지 않다. 그래서 C에서 사용하는 동적할당 API를 호환하여 사용한다.

  • malloc() : 동적으로 메모리를 할당하는 함수
  • calloc() : malloc와 같은 기능, 다른 형태의 함수
  • realloc() : 이미 할당한 공간의 크기를 바꿔주는 함수
  • free() : 힙 영역에 할당된 메모리를 해제하는 함수



파이썬에서 메모리 할당과 해제

할당(동작 시 메모리 할당)

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에서 변수할당과 사용자 지정 함수를 동작시키고 있다.

  1. 먼저 y가 int객체 5를 가르킨다.
  2. f1(y)를 동작시키면 먼저 Stack 영역에 f1()영역이 생성된다.
  3. f1()영역에서 x=x*2로 인해 x는 int객체 10을 가르키게 된다.
  4. 밑에 줄에선 f2(x)를 호출하면서 Stack영역에 f2()이 생성된다.
  5. f2()영역에서 x=x+1로 int 객체 11이 생성되고 y는 그것을 가르키게 된다.


해제(동작이 완료되면 해제)

  1. Stack은 LIFO이므로 가장 위에 있는 f2()가 먼저 해제된다.

  1. 그 다음 f1()이 해제되는데, f1()의 변수 x가 없어짐에 따라 int 객체 10도 함께 사라진다.

왜냐하면 int 객체 10을 아무도 가르키고있지 않기 때문이다. 이것이 reference counting이 0이 됨에 따라 object가 사라지는 Garbage collecoting이다.
❗️파이썬은 reference counting을 이용해 메모리를 관리한다.

  1. 마지막으로 main의 변수 z가 11을 가리키게 된다.



메모리 재할당

  • 메모리 재할당은 필요한 메모리의 양이 바뀌어서 다시 메모리를 할당 받는 것이다.

  • 메모리 재할당 시 뒷편에 공간이 충분하다면 그냥 공간을 늘려주면 된다.
    그러나 메모리 재할당 과정시 뒷편에 메모리에 충분한 공간이 없다면 데이터 할당영역을 옮겨서 재할당해야한다.

  • 빈번한 메모리 재할당은 새로 메모리를 할당받아야하므로 가급적 피하는 것이 좋다.

  • 한번에 깔끔하게 메모리를 받아내는 것이 좋다.

  • for문 속에서 append를 사용하게 되면 메모리 재할당이 이루어져서 메모리를 효율적으로 사용하지 못한다.




참조

profile
velog에는 이론을 주로 정리하고, 코드와 관련된 것은 Git-hub로 관리하고 있어요. 포트폴리오는 링크된 Yun Lab 홈페이지를 참고해주시면 감사하겠습니다!

0개의 댓글