영넌 개발로그
[CS] Python 메모리 구조 및 관리 본문
메모리 관리
모든 파이썬 객체와 데이터 구조를 포함하는 비공개 힙(private heap)은 python memory manager가 비공개적으로 알아서 관리한다.
- 메모리 관리를 위해 숨겨진 힙 스페이스 사용한다.
- python memory manger는 힙 메모리에 있는 객체를 참조하는 형태로 동적할당을 자동으로 해준다
- 인터프리터가 스페이스를 관리하기 때문에 프로그래머 조차도 이 공간에 접근이 불가능 하다.
- 인터프리터가 포인터를 사용하여 힙 메모리 영역의 범위를 조정, 메모리가 필요할때마다 OS와 소통하면서 할당
- 빌트인 가비지 컬렉터(Garbage Collector; GC)를 소유하고 있다.
- GC를 이용하여 사용되지 않은 메모리를 재활용하고 메모리를 지워 힙 스페이스에서 사용 가능케 한다.
메모리 구조
정적 메모리동적 메모리
정적 메모리 | 동적 메모리 |
컴파일 시 메모리 할당 | 런타임 시 메모리 할당 |
고정 크기로만 정적 배열 선언 | 연산자를 사용하여 배열 선언 |
스택은 정적 할당을 구현하는 데 사용 | 힙은 동적 할당을 구현하는 데 사용 |
메모리 재사용 불가능 | 메모리 재사용 가능 |
- 스택(stack) 영역
- 지역변수/ 매개변수 저장
- 함수가 호출될 때 할당되고 호출이 끝나면 소멸
- 참조자 저장
- 힙(heap) 영역
- new 명령으로 생성된 인스턴스 변수(사용자의 동적할당)가 저장
- 메소드 호출이 끝나도 소멸되지 않음
- 객체 저장
- 데이터(data) 영역
- 전역변수/정적변수 저장
- 프로그램이 시작될 때 할당되고 프로그램이 종료되면 소멸
- 코드(text) 영역
- 실행할 프로그램의 코드 저장
Garbage Collector
GC는 메모리를 자동으로 관리해주는 '과정'이다.
자동으로 메로리를 관리해주니 사람이 직접하는 것보다 최적화가 덜 되어 있다.
공부하여 업무에 적용해야할 필요성 존재
(인스타그램은 Python GC를 사용하지 않음 : https://luavis.me/python/dismissing-python-garbage-collection-at-instagram)
동기적인 코드(CS,ML)는 메모리 관리를 크게 신경쓰지 않아도 되지만
비동기적인 코드(Cloud, DB, Backend, Frontend)는 메모리 관리에 노력해야 한다.
성능이 좋아야 하는 장기 (long-running) 프로그램의 경우, 일부 언어(C++, Objective-C)에는 여전히 수동 메모리 관리 기능을 사용한다.
작동 방식
파이썬은 메모리 관리를 위한 두 가지 방법 존재
1. 레퍼런스 카운팅 (Reference Counting)
- sys 모듈을 사용하여 특정 객체의 레퍼런스 카운트 확인 가능 sys.getrefcount(변수)
- 객체를 참조할 때마다 해당 객체의 레퍼런스 카운트는 증가, 참조 횟수가 줄어들수록 감소
- 객체의 레퍼런스 카운트가 0이 되면 객체는 메모리에서 해제
- 단, 레퍼런스 카운트가 0이 아닌 경우에도 자가 참조 혹은 삭제된 객체들이 순환 참조되어 도달할 수 없는 경우에도 메모리에서 해제
참조 횟수를 증가시키는 방법
- 변수에 객체 할당
- list에 추가하거나 class instance에서 속성으로 추가하는 등의 data structure에 객체 추가
- 객체를 함수의 인수로 전달
문제점
- 객체가 순환 참조할 경우
a=[]
a.append(a)
del a
a의 참조 횟수는 1이지만 이 객체는 더 이상 접근할 수 없으며 레퍼런스 카운팅 방식으로는 메모리에서 해제 될 수 없다.
2. 다른 객체가 서로를 참조할 경우
a = Func_pr() # 0x01
b = Func_pr() # 0x02
a.x = b # 0x01의 x는 0x02를 가리킨다.
b.x = a # 0x02의 x는 0x01를 가리킨다.
# 0x01의 레퍼런스 카운트는 a와 b.x로 2다.
# 0x02의 레퍼런스 카운트는 b와 a.x로 2다.
del a # 0x01은 1로 감소한다. 0x02는 b와 0x01.x로 2다.
del b # 0x02는 1로 감소한다.
마지막 상태에서 0x01.x와 0x02.x가 서로를 참조하고 있기 때문에 레퍼런스 카운트는 둘 다 1이지만 0에 도달할 수 없는 garbage가 된다.
이러한 유형의 문제를 reference cycle(참조 주기)라고 하며 레퍼런스 카운팅으로 해결할 수 없다.
2. 세대별 가비지 컬렉션 (Generational Garbage Collection)
- 레퍼런스 카운팅이 주로 사용되는 방법, 보조로 GC 사용
- 가비지 컬렉터는 세대(generation)와 임계값(threshold)을 통해 가비지 컬렉션 주기와 객체를 관리한다.
- 세대는 0~2세대로 구분되고 최근 생성된 객체는 0세대(young)에 들어가고 오래된 객체일수록 2세대(old)로 이동한다.
- 한 객체는 단 하나의 세대에만 속한다.
- 0세대일수록 더 자주 가비지 컬렉션을 하도록 설계되어 있는데 generational hypothesis에 근거한다.
(https://plumbr.io/handbook/garbage-collection-in-java/generational-hypothesis) - python 가비지 수집기는 총 3세대이며, 객체는 현재 세대의 가비지 수집 프로세스에서 살아 남을때마다 이전 세대로 이동한다.
- 각 세대마다 가비지 컬렉터 모듈에는 임계값 개수의 개체가 있다.
- 객체 수가 해당 임계값을 초과하면 가비지 콜렉션이 콜렉션 프로세스를 trigger(추적)한다.
- 해당 프로세스에서 살아남은 객체는 이전 세대로 이동한다.
- python 프로그램에서 세대 가비지 컬렉터의 동작을 변경할 수 있다.
- 'garbage collection process를 trigger 하기 위한 임계값 변경', '가비지 컬렉션 프로세스를 수동으로 trigger 하는 것', '가비지 컬렉션 프로세스를 모두 비활성화하는 것' 가능
GC가 성능에 영향을 주는 이유?
- GC를 수행하려면 응용 프로그램을 완전히 중지해야 한다. 그러므로 객체가 많을수록 모든 가비지를 수집하는 데 시간이 오래 걸린다는 것을 의미한다.
- GC 주기가 짧다면 응용 프로그램이 중지되는 상황이 증가하고 반대로 주기가 길어진다면 메모리 공간에 garbage가 많이 쌓이게 된다.
- 이는 시행착오를 거치며 응용 프로그램의 성능을 끌어 올려야 한다.
import gc
GC module 사용 ::- gc.get_threshold() : 가비지 컬렉터의 구성된 임계값 확인
- 결과 : (threshold 0, threshold 1, threshold 2)
- 해석 : n세대에 객체를 할당한 횟수가 threshold n을 초과하면 가비지 컬렉션이 수행된다.
- gc.get_count() : 각 세대의 객체 수 확인
- gc.collect() : 수동 가비지 콜렉션 프로세스 추적
- 파이썬은 프로그램을 시작하기 전에 기본적으로 많은 객체를 생성하므로 한 번 실행해주면 객체가 정리된다.
- gc.set_threshold() : 가비지 컬렉션 트리거 임계값 변경 가능
- 임계 값을 증가시키면 가비지 컬렉션이 실행되는 빈도가 줄어든다.
- 죽은 객체를 오래 유지하는 cost(비용)로 프로그램에서 계산 비용이 줄어든다.
Manual Garbage Collection (수동 가비지 컬렉션)
수동 메모리 관리는 컴퓨터 자원이 제한된 환경에 적합하다.
- Time-based : 가비지 컬렉터를 고정된 시간 간격마다 호출
- Event-based : 이벤트 발생시 가비지 컬렉터 호출
ex) 사용자가 응용 프로그램 종료, 응용 프로그램이 중단 상태일 경우
주의사항 !
GC를 수정하는 것보다 컴퓨터 자원을 증가시키는 편이 훨씬 좋다.
메모리를 확보하기 위해 수행하는 수동 가비지 컬렉션 프로세스는 원하지 않는 결과가 나올 수 있다.
ㄴ python이 일반적으로 운영 체제 메모리를 다시 release 하지 않는다는 사실을 고려
레퍼런스 카운트는 비활성화할 수 없다.
먼저 Stackify's Retrace와 같은 툴로 응용 프로그램 성능 및 문제를 정확하게 파악하는 것이 중요하다.
문제를 파악했다면 다양한 튜닝을 통해 해결하면 된다.
Stackify's Retrace 사이트 : https://stackify.com/retrace/
파이썬 리스트/배열/튜플/클래스 내부구조 및 메모리 할당
https://kadensungbincho.tistory.com/59
Reference
[파이썬 메모리 공식문서] https://docs.python.org/ko/3/c-api/memory.html
https://medium.com/dmsfordsm/garbage-collection-in-python-777916fd3189
https://rushter.com/blog/python-memory-managment/
'알고리즘 연습 > 이론' 카테고리의 다른 글
[CS] Python 특징 (0) | 2023.06.18 |
---|---|
[알고리즘] 해시함수, 뻐꾸기 해싱 Cuckoo Hashing (재해시) (1) | 2020.11.08 |
[C언어 알고리즘] Recursion 재귀함수 (0) | 2020.09.13 |
알고리즘 복잡도 (시간 복잡도) (0) | 2020.09.13 |