새소식

반응형
Programming Language/Python

[Python] 파이썬 메모리 구조

  • -
반응형

1. Everything is an object in Python


"Everything is an object in Python"은 파이썬의 핵심 철학 중 하나를 나타내는 슬로건이다. 이 슬로건은 파이썬 언어가 객체 지향 프로그래밍(Object-Oriented Programming, OOP)의 원칙을 엄격하게 따르고 있다는 것을 강조한다. 이 말은 파이썬에서 모든 것, 즉 숫자, 문자열, 함수, 클래스, 모듈, 심지어 파이썬 자체의 기본 데이터 타입도 객체로 취급된다는 것을 의미한다. 파이썬에서 "객체"는 데이터와 그 데이터를 조작하는 메서드(함수)를 포함하는 것을 의미한다. 다른 언어에서는 원시 데이터 타입과 객체를 구분하기도 하지만, 파이썬에서는 이러한 구분이 없다. 모든 것이 객체이며, 모든 객체는 클래스로부터 생성된다. 예를 들어, 정수, 실수, 문자열, 리스트, 함수 등 모든 것은 각각의 클래스에 속하며 객체로 취급된다. 이 철학은 파이썬의 일관성과 간결성을 높이고, 객체 지향 프로그래밍의 개념을 파이썬 코드 작성에 적용하는 데 도움을 준다. 또한 파이썬에서는 객체의 메서드를 호출하거나 속성에 접근하는 일반적인 방식으로 모든 데이터와 기능을 다룰 수 있으므로 코드가 더욱 읽기 쉽고 확장 가능하게 된다.
 

x = 29 # 정수 29를 변수 x에 할당
print(type(x))
<class 'int'>

다른 언어에서 위와 같이 변수에 데이터를 할당하게 되면 메모리에 해당 값이 저장되는 방식이지만 파이썬에서는 int라는 object를 만들어서 해당 object의 주소를 변수에 할당하게 된다.
 

x = 29
y = x

print(f'id(x) : {id(x)}')
print(f'id(y) : {id(y)}')

if id(x) == id(y):
    print("둘의 메모리 주소는 같습니다")
id(x) : 140734850332328
id(y) : 140734850332328    
둘의 메모리 주소는 같습니다

x에 29라는 정수형 데이터는 이미 할당이 되었다, 이 말인즉슨, 29라는 데이터는 메모리 어딘가에 할당이 되었고 해당 메모리의 주소를 x에 저장하게 된다. 그래서 y에 x를 대입해도 해당 메모리의 주소가 y에 할당하게 되고 그 메모리의 주소에는 29라는 정수형 데이터가 저장되어 있다. 그래서 x와 y의 메모리 주소값을 비교했을 때 True값을 반환하게 된다.
 
 

x와 y가 가리키는 주소가 같다

x = 29
y = x + 1

print(f'id(x) : {id(x)}')
print(f'id(y) : {id(y)}')

if id(x) == id(y):
    print("둘의 메모리 주소는 같습니다")
else:
    print("둘의 메모리 주소는 같지 않습니다")

하지만 y에 x를 대입하면서 1을 더하게 되면 30이라는 정수형 데이터가 새로운 메모리 주소에 할당하게 된다.
 

id(x) : 140734850332328
id(y) : 140734850332360
둘의 메모리 주소는 같지 않습니다

위의 결과에서 확인할 수 있듯이 y에 x를 대입하면서 +1을 했더니 새로운 메모리 주소가 할당이 되고 그 메모리 주소에 30이라는 새로운 데이터가 저장된다, 여기서 강조하고 싶은 건 변수에는 30이라는 데이터가 저장되는 게 아니라 데이터가 저장돼 있는 메모리의 주소가 저장된다.
 
 

y에 새로운 메모리 주소 할당

x = 29
y = x + 1

z = 29

print(f'id(x) : {id(x)}')
print(f'id(y) : {id(y)}')
print(f'id(z) : {id(z)}')

if id(x) == id(z):
    print("둘의 메모리 주소는 같습니다")
else:
    print("둘의 메모리 주소는 같지 않습니다")
id(x) : 140723630503592
id(y) : 140723630503624    
id(z) : 140723630503592    
둘의 메모리 주소는 같습니다

이어서 새로운 변수 z를 만들고 그곳에 x에 넣었던 똑같은 값인 29를 넣어보았다, 29는 이미 메모리에 저장되어 있기 때문에 새로운 메모리 공간을 할당해서 데이터를 저장할 필요가 없어 z에 기존에 이미 생성되어 있던 x의 주소가 할당되는 걸 확인할 수 있다.
 

지금까지는 정수형 변수로만 예시로 들었지만 위에서 언급했다시피 모든 오브젝트(정수, 실수, 문자열, 리스트, 함수 등)는 클래스이며 해당 데이터의 메모리의 주소가 변수에 저장된다.
 
 

2. 파이썬의 메모리 구조


파이썬은 다양한 메모리 영역을 사용하여 프로그램을 실행하고 데이터를 관리한다. 이러한 메모리 영역은 파이썬 인터프리터와 관련된 작업을 수행하며, 파이썬 프로그램이 메모리를 효율적으로 사용하고 데이터를 처리하는 데 도움이 된다. 다음은 파이썬의 주요 메모리 영역에 대한 설명이다.
 

1. 스택(Stack)

• 스택 메모리 영역은 함수 호출과 관련된 데이터를 저장하는 데 사용된다.
• 함수가 호출될 때, 해당 함수의 지역 변수 및 함수 관련 정보(복귀 주소 등)가 스택에 저장된다.
• 함수가 실행을 완료하면 해당 함수의 스택 프레임이 제거되고, 스택에서 데이터가 팝 된다.
• 스택은 후입선출(LIFO, Last-In-First-Out) 구조를 가지며, 재귀 함수 호출과 함수 호출 중에 발생하는 작업을 관리한다.
 

2. 힙(Heap)

• 힙 메모리 영역은 동적으로 할당된 데이터, 주로 객체와 데이터 구조를 저장하는 데 사용된다.
• 파이썬의 가비지 컬렉션(Garbage Collection)은 힙에서 더 이상 필요하지 않은 객체를 자동으로 관리하며 메모를 해제한다.
• 리스트, 딕셔너리, 클래스 인스턴스 등의 객체는 주로 힙에 저장된다.
 

3. 데이터 섹션

• 데이터 섹션은 정적 데이터와 전역 변수를 저장하는 데 사용된다.
• 프로그램이 시작될 때 할당되어 프로그램 종료 시 해제된다.
 

4. 텍스트 섹션(Text Section 또는 코드 섹션)

• 텍스트 섹션은 실행 가능한 코드를 저장하는 데 사용된다.
• 파이썬 인터프리터 및 프로그램 코드가 저장된다.
 

5. 상수 영역(Constant Pool)

• 상수 영역은 문자열 리터럴과 같은 상수 데이터를 저장하는 데 사용된다.
• 이러한 상수 데이터는 프로그램 실행 중에 변경되지 않는다.
 

6. 스태틱 메모리(Static Memory)

• 스태틱 메모리는 정적 변수와 클래스 변수를 저장하는 데 사용된다.
• 정적 변수는 함수 내에서 선언되지 않고 프로그램 전체에서 접근 가능한 변수이다.
• 클래스 변수는 클래스의 모든 인스턴스에서 공유되는 변수이다.
 
파이썬의 메모리 관리는 가비지 컬렉션을 통해 메모리 누수를 방지하고 자동으로 메모리를 관리한다. 프로그래머는 주로 객체 생성 및 삭제와 관련된 메모리 관리에 신경 쓰며, 파이썬 인터프린터는 나머지 메모리 영역을 효율적으로 관리한다.
 
 

3. 파이썬에서의 Heap 사용


위에서 잠깐 언급했다시피 파이썬에서 힙(Heap)은 동적으로 할당된 데이터를 저장하는 메모리 영역이다. 주로 객체와 데이터 구조가 힙에 저장되며, 파이썬의 가비지 컬렉션(Garbage Collection) 기능을 통해 더 이상 필요하지 않은 객체를 자동으로 관리하고 메모리를 해제한다. 힙은 변수에 할당된 객체가 어떤 값을 가지고 있더라도 해당 값을 저장하기 위해 사용된다.
 
파이썬에서 힙을 사용하는 예시 코드를 한 번 살펴보자.

# 리스트에 값을 할당하고 해당 리스트를 힙에 저장한다.
my_list = [1, 2, 3, 4, 5]

# 딕셔너리에 키-값 쌍을 할당하고 해당 딕셔너리를 힙에 저장한다.
my_dict = {'name': 'John', 'age': 30, 'city': 'New York'}

# 클래스를 정의하고 클래스의 인스턴스를 힙에 저장한다.
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# Person 클래스의 인스턴스를 생성하고 힙에 저장한다.
person1 = Person('Jack', 29)
person2 = Person('Snider', 30)

# 힙에 저장된 객체에 접근한다.
print(my_list)     # [1, 2, 3, 4, 5]
print(my_dict)     # {'name': 'Jack', 'age': 29, 'city': 'Daejeon'}
print(person1.name, person1.age)  # Jack 29
print(person2.name, person2.age)  # Snider 30

# 객체를 변수에서 삭제하면 가비지 컬렉션에 의해 자동으로 메모리가 해제된다.
del my_list
del my_dict
del person1
del person2

위의 예시에서는 리스트, 딕셔너리, 클래스 인스턴스를 생성하고 해당 객체를 힙에 저장하였다. 객체를 변수에서 삭제하면 파이썬의 가비지 컬렉션 메커니즘이 해당 객체를 추적하고 더 이상 참조되지 않을 때 자동으로 메모리를 해제한다. 이를테면 del 키워드를 사용하여 변수를 삭제함으로써 객체를 참조하지 않게 만들 수 있다.
 
파이썬의 힙 관리는 개발자에게 메모리 관리 부담을 덜어주며, 메모리 누수를 방지하기 위해 유용한 기능 중 하나이다.
 
 

4. 파이썬에서의 Stack 영역


파이썬에서의 스택(Stack) 영역은 주로 함수 호출과 관련된 데이터를 저장하는 메모리 영역이다. 함수가 호출될 때 해당 함수의 지역 변수, 복귀 주소 및 관련 정보가 스택에 저장되며, 함수가 실행을 완료하면 해당 함수의 스택 프레임이 제거되고 스택에서 데이터가 팝 된다. 스택은 후입선출(LIFO, Last-In-First-Out) 구조를 가지고 있다.
 
다음은 파이썬에서의 스택 사용에 대한 예시 코드를 한 번 살펴보자.

def greet(name):
    # 함수 greet의 스택 프레임이 생성되고 name 변수가 스택에 저장된다.
    message = f"Hello, {name}!"
    print(message)  # "Hello, Jack!" 출력

def bye(name):
    message = f'Goodbye, {name}!'
    print(message) # "Goddbye, Jack!" 출력


# 함수 greet를 호출하면 새로운 스택 프레임이 생성된다.
greet("Jack")
bye("Jack")

# 함수가 실행을 완료하면 스택 프레임이 제거되고 스택에서 데이터가 팝된다.
# 이후 다음 코드를 실행할 때 스택은 비어 있다.

위에 예시에서 greet 함수와 bye함수가 호출될 때마다 해당 함수의 스택 프레임이 생성되고 name 변수와 message 변수가 스택에 저장된다. 함수가 실행을 완료하면 해당 스택 프레임이 제거되고 스택에서 데이터가 팝 된다.
 

 
great() 함수가 먼저 실행이 되고 후에 bye() 함수가 실행이 되므로 bye() 함수가 스택에 가장 늦게 할당되어 제일 위에 있게 된다, 그리고 스택에서 할당이 해제될 때는 가장 위에 있는 bye() 함수가 해제되고 greet() 함수가 해제되게 된다.
 
스택은 주로 함수 호출을 관리하기 위해 사용되며, 재귀 함수 호출과 함수 호출 중에 발생하는 작업을 처리하는 데 중요한 역할을 한다. 함수가 중첩되거나 호출 스택이 깊어지면 파이썬 인터프리터는 이러한 호출을 관리하고 스택을 사용하여 함수 호출의 순서를 추적한다.

728x90
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.